Skip to content

Commit bbd52da

Browse files
authored
Merge pull request #170 from link-foundation/issue-165-c563cc7b8214
Publish JavaScript package to npm
2 parents 1271fb5 + 60d0f86 commit bbd52da

8 files changed

Lines changed: 140 additions & 11 deletions

File tree

.github/workflows/js.yml

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@ on:
99
- 'parity/**'
1010
- '.github/workflows/js.yml'
1111
pull_request:
12+
types: [opened, synchronize, reopened]
1213
paths:
1314
- 'js/**'
1415
- 'parity/**'
1516
- '.github/workflows/js.yml'
17+
release:
18+
types: [published]
1619
workflow_dispatch:
1720

1821
concurrency:
@@ -49,3 +52,77 @@ jobs:
4952
- name: Verify npm package contents
5053
run: npm pack --dry-run
5154
working-directory: js
55+
56+
publish:
57+
name: Publish npm Package
58+
needs: test
59+
if: |
60+
github.event_name == 'workflow_dispatch' ||
61+
(github.event_name == 'release' && github.event.action == 'published')
62+
runs-on: ubuntu-latest
63+
timeout-minutes: 30
64+
permissions:
65+
contents: read
66+
id-token: write
67+
steps:
68+
- uses: actions/checkout@v6
69+
70+
- name: Setup Node.js
71+
uses: actions/setup-node@v6
72+
with:
73+
node-version: 24
74+
registry-url: 'https://registry.npmjs.org'
75+
76+
- name: Update npm for trusted publishing
77+
run: |
78+
npm install -g npm@11
79+
npm --version
80+
81+
- name: Read package metadata
82+
id: package
83+
working-directory: js
84+
run: |
85+
name="$(node -p "require('./package.json').name")"
86+
version="$(node -p "require('./package.json').version")"
87+
88+
if [ "$name" != "meta-language" ]; then
89+
echo "::error::Expected package name meta-language, got $name"
90+
exit 1
91+
fi
92+
93+
echo "name=$name" >> "$GITHUB_OUTPUT"
94+
echo "version=$version" >> "$GITHUB_OUTPUT"
95+
echo "Package: $name@$version"
96+
97+
- name: Verify release tag matches package version
98+
if: github.event_name == 'release'
99+
env:
100+
PACKAGE_VERSION: ${{ steps.package.outputs.version }}
101+
RELEASE_TAG: ${{ github.event.release.tag_name }}
102+
run: |
103+
normalized_tag="${RELEASE_TAG#v}"
104+
if [ "$normalized_tag" != "$PACKAGE_VERSION" ]; then
105+
echo "::error::Release tag $RELEASE_TAG does not match js/package.json version $PACKAGE_VERSION"
106+
exit 1
107+
fi
108+
109+
- name: Check npm registry
110+
id: npm
111+
working-directory: js
112+
run: |
113+
if npm view meta-language@${{ steps.package.outputs.version }} version; then
114+
echo "already_published=true" >> "$GITHUB_OUTPUT"
115+
else
116+
echo "already_published=false" >> "$GITHUB_OUTPUT"
117+
fi
118+
119+
- name: Publish to npm
120+
if: steps.npm.outputs.already_published != 'true'
121+
env:
122+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
123+
run: npm publish --provenance
124+
working-directory: js
125+
126+
- name: Verify published package
127+
if: steps.npm.outputs.already_published != 'true'
128+
run: npm view meta-language@${{ steps.package.outputs.version }} version

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ in **both Rust and JavaScript** with guaranteed feature parity between the two.
66
[![Rust](https://github.com/link-foundation/meta-language/actions/workflows/rust.yml/badge.svg)](https://github.com/link-foundation/meta-language/actions/workflows/rust.yml)
77
[![JavaScript](https://github.com/link-foundation/meta-language/actions/workflows/js.yml/badge.svg)](https://github.com/link-foundation/meta-language/actions/workflows/js.yml)
88
[![Crates.io](https://img.shields.io/crates/v/meta-language?label=crates.io&style=flat)](https://crates.io/crates/meta-language)
9-
[![npm](https://img.shields.io/npm/v/@link-foundation/meta-language?label=npm&style=flat)](https://www.npmjs.com/package/@link-foundation/meta-language)
9+
[![npm](https://img.shields.io/npm/v/meta-language?label=npm&style=flat)](https://www.npmjs.com/package/meta-language)
1010
[![License: Unlicense](https://img.shields.io/badge/license-Unlicense-blue.svg)](https://unlicense.org/)
1111

1212
**Website:** <https://link-foundation.github.io/meta-language> — project
@@ -22,7 +22,7 @@ badges, and its own CI/CD workflow.
2222
| Path | Contents |
2323
| --- | --- |
2424
| [`rust/`](rust/README.md) | Rust crate `meta-language` — the reference implementation. Built and tested by [`.github/workflows/rust.yml`](.github/workflows/rust.yml). |
25-
| [`js/`](js/README.md) | JavaScript package `@link-foundation/meta-language`. Built and tested by [`.github/workflows/js.yml`](.github/workflows/js.yml). |
25+
| [`js/`](js/README.md) | JavaScript package `meta-language`. Built and tested by [`.github/workflows/js.yml`](.github/workflows/js.yml). |
2626
| [`parity/`](parity/language-features.json) | Cross-language feature manifest. Every feature must be present in both languages (see [Feature parity](#feature-parity)). |
2727
| [`docs/`](docs) | Shared documentation: the grammar subsystem, fidelity matrices, the project website source, and per-issue case studies. |
2828
| [`.github/`](.github/workflows) | Shared CI/CD workflows (`rust.yml`, `js.yml`). |
@@ -56,7 +56,7 @@ npm test
5656
```
5757

5858
```js
59-
import { LinkNetwork, ParseConfiguration } from '@link-foundation/meta-language';
59+
import { LinkNetwork, ParseConfiguration } from 'meta-language';
6060

6161
const network = LinkNetwork.parse('alpha beta', 'txt', ParseConfiguration.default());
6262
console.log(network.reconstructText()); // alpha beta

docs/case-studies/issue-163/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ repository file inventories are stored in `template-data/`.
1414
language implementation lives at the root**. The crate keeps its own `src/`,
1515
`tests/`, `scripts/`, `web/`, `benches/`, `examples/`, `Cargo.toml`,
1616
`README.md`, and badges.
17-
- Added `js/` as an npm package named `@link-foundation/meta-language` with its
17+
- Added `js/` as an npm package named `meta-language` with its
1818
own `src/`, `tests/`, `scripts/`, `README.md`, and badges.
1919
- Implemented the parity operation families in JavaScript: parse, query,
2020
transform, substitute, serialize, snapshot, translate, and verify.

js/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
# @link-foundation/meta-language (JavaScript)
1+
# meta-language (JavaScript)
22

33
[![JavaScript](https://github.com/link-foundation/meta-language/actions/workflows/js.yml/badge.svg)](https://github.com/link-foundation/meta-language/actions/workflows/js.yml)
4-
[![npm](https://img.shields.io/npm/v/@link-foundation/meta-language?label=npm&style=flat)](https://www.npmjs.com/package/@link-foundation/meta-language)
4+
[![npm](https://img.shields.io/npm/v/meta-language?label=npm&style=flat)](https://www.npmjs.com/package/meta-language)
55
[![Node.js Version](https://img.shields.io/badge/node-%3E%3D20-blue.svg)](https://nodejs.org/)
66
[![License: Unlicense](https://img.shields.io/badge/license-Unlicense-blue.svg)](https://unlicense.org/)
77

@@ -26,7 +26,7 @@ import {
2626
LinkQuery,
2727
ParseConfiguration,
2828
ReplacementRule,
29-
} from '@link-foundation/meta-language';
29+
} from 'meta-language';
3030

3131
const network = LinkNetwork.parse(
3232
'const oldName = call(oldName);\n',

js/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

js/package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "@link-foundation/meta-language",
2+
"name": "meta-language",
33
"version": "0.46.0",
44
"description": "JavaScript implementation of the meta-language links-network core",
55
"type": "module",
@@ -28,6 +28,9 @@
2828
"lino"
2929
],
3030
"license": "Unlicense",
31+
"publishConfig": {
32+
"access": "public"
33+
},
3134
"repository": {
3235
"type": "git",
3336
"url": "https://github.com/link-foundation/meta-language.git",

js/tests/package-release.test.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import assert from 'node:assert/strict';
2+
import { readFile } from 'node:fs/promises';
3+
import { test } from 'node:test';
4+
5+
async function readJson(url) {
6+
return JSON.parse(await readFile(url, 'utf8'));
7+
}
8+
9+
test('npm package metadata uses the public unscoped package name', async () => {
10+
const packageJson = await readJson(new URL('../package.json', import.meta.url));
11+
const packageLock = await readJson(new URL('../package-lock.json', import.meta.url));
12+
const rootReadme = await readFile(new URL('../../README.md', import.meta.url), 'utf8');
13+
const jsReadme = await readFile(new URL('../README.md', import.meta.url), 'utf8');
14+
const rustReadme = await readFile(new URL('../../rust/README.md', import.meta.url), 'utf8');
15+
const issue163CaseStudy = await readFile(
16+
new URL('../../docs/case-studies/issue-163/README.md', import.meta.url),
17+
'utf8',
18+
);
19+
20+
assert.equal(packageJson.name, 'meta-language');
21+
assert.equal(packageLock.name, 'meta-language');
22+
assert.equal(packageLock.packages[''].name, 'meta-language');
23+
24+
for (const readmeWithBadge of [rootReadme, jsReadme]) {
25+
assert.ok(readmeWithBadge.includes('npmjs.com/package/meta-language'));
26+
}
27+
28+
for (const readmeWithImport of [rootReadme, jsReadme, rustReadme]) {
29+
assert.ok(readmeWithImport.includes("from 'meta-language'"));
30+
}
31+
32+
for (const publicDoc of [rootReadme, jsReadme, rustReadme, issue163CaseStudy]) {
33+
assert.equal(publicDoc.includes('@link-foundation/meta-language'), false);
34+
}
35+
});
36+
37+
test('JavaScript workflow publishes to npm with trusted publishing provenance', async () => {
38+
const workflow = await readFile(
39+
new URL('../../.github/workflows/js.yml', import.meta.url),
40+
'utf8',
41+
);
42+
43+
assert.match(workflow, /release:\s*\n\s+types:\s+\[published\]/);
44+
assert.match(workflow, /id-token:\s+write/);
45+
assert.match(workflow, /registry-url:\s+['"]https:\/\/registry\.npmjs\.org['"]/);
46+
assert.match(workflow, /working-directory:\s+js/);
47+
assert.match(workflow, /npm publish --provenance/);
48+
assert.match(workflow, /npm view meta-language@\$\{\{\s*steps\.package\.outputs\.version\s*\}\} version/);
49+
});

rust/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ npm test
142142
```
143143

144144
```js
145-
import { LinkNetwork, ParseConfiguration } from '@link-foundation/meta-language';
145+
import { LinkNetwork, ParseConfiguration } from 'meta-language';
146146

147147
const network = LinkNetwork.parse('alpha beta', 'txt', ParseConfiguration.default());
148148

0 commit comments

Comments
 (0)