diff --git a/renderers/angular/package.json b/renderers/angular/package.json index 136736d2a..b2c3b313b 100644 --- a/renderers/angular/package.json +++ b/renderers/angular/package.json @@ -3,7 +3,9 @@ "version": "0.8.4", "license": "Apache-2.0", "scripts": { - "build": "ng build && node postprocess-build.mjs" + "build": "ng build && node postprocess-build.mjs", + "prepublishOnly": "node -e \"if(!process.cwd().endsWith('dist')) { console.error('Error: This package must be published from the dist/ directory. Run `npm run build` then `npm publish dist/`'); process.exit(1); }\"", + "publish:package": "npm run build && npm publish dist/ --access public" }, "dependencies": { "@a2ui/web_core": "file:../web_core", diff --git a/renderers/angular/postprocess-build.mjs b/renderers/angular/postprocess-build.mjs index e31d44b69..497f924dc 100644 --- a/renderers/angular/postprocess-build.mjs +++ b/renderers/angular/postprocess-build.mjs @@ -38,6 +38,12 @@ if (!packageJson.dependencies['@a2ui/web_core']) { } packageJson.dependencies['@a2ui/web_core'] = '^' + coreVersion; + +// Remove scripts and properties that should not be in the published package +delete packageJson.scripts; +delete packageJson.prepublishOnly; +delete packageJson.files; + writeFileSync(packageJsonPath, JSON.stringify(packageJson, undefined, 2)); copyFileSync(join(dirname, '../../LICENSE'), join(dirname, './dist/LICENSE')); diff --git a/renderers/docs/web_publishing.md b/renderers/docs/web_publishing.md new file mode 100644 index 000000000..00fb455af --- /dev/null +++ b/renderers/docs/web_publishing.md @@ -0,0 +1,124 @@ +# Publishing Guide for A2UI Web Packages + +This guide is for project maintainers. It details the manual publishing process to the npm registry for all four web-related packages in this repository: + +1. `@a2ui/web_core` +2. `@a2ui/lit` +3. `@a2ui/angular` +4. `@a2ui/markdown-it` + +--- + +## 🚀 Setup Authentication + +Ensure you have an NPM Access Token with rights to the `@a2ui` organization. + +1. Create an `.npmrc` file in the directory of the package you are publishing (it is git-ignored): + ```sh + echo "//registry.npmjs.org/:_authToken=\${NPM_TOKEN}" > .npmrc + ``` +2. Export your token in your terminal: + ```sh + export NPM_TOKEN="npm_YourSecretTokenHere" + ``` + +--- + +## 📦 1. Publishing `@a2ui/web_core` + +This package does not have internal `file:` dependencies, so it can be published directly from its root. + +### Pre-flight Checks +1. Ensure your working tree is clean and you are on the correct branch (e.g., `main`). +2. Update the `version` in `renderers/web_core/package.json`. +3. Verify all tests pass: + ```sh + cd renderers/web_core + npm run test + ``` + +### Publish to NPM +Because this is a scoped package (`@a2ui/`), you have two options: + +**Option A: Publish as Private, then promote to Public (Requires Paid NPM Account)** +1. Publish (defaults to private): + ```sh + npm publish + ``` +2. Verify the package looks correct on the npm website. +3. Promote to public: + ```sh + npm access public @a2ui/web_core + ``` + +**Option B: Publish directly as Public (Free or Paid NPM Account)** +```sh +npm publish --access public +``` + +*Note: NPM automatically executes the `prepack` script (`npm run build`), compiles the TypeScript, and generates the `dist/` directory right before creating the tarball.* + +**What exactly gets published?** +Only the `dist/` directory, `src/` directory (for sourcemaps), `package.json`, `README.md`, and `LICENSE` are included in the published package. This is strictly controlled by the `"files"` array in `package.json`. Internal files like this publishing guide, tests, and configuration scripts are excluded. + +**What about the License?** +The package is automatically published under the `Apache-2.0` open-source license, as defined in `package.json`. + +--- + +## 📦 2. Publishing `@a2ui/lit`, `@a2ui/angular`, and `@a2ui/markdown-it` + +These packages depend on `@a2ui/web_core` via a local `file:../web_core` path for development. Therefore, **they must be published from their generated `dist/` folders.** We use specialized scripts to automatically rewrite their `package.json` with the correct `@a2ui/web_core` npm version before publishing. + +If you attempt to run `npm publish` in their root directories, it will fail and throw an error to protect against publishing broken paths. + +### Pre-flight Checks +1. Ensure `@a2ui/web_core` is already published (or its version string is correctly updated) since these packages will read that version number. +2. Update the `version` in the package you want to publish (e.g., `renderers/lit/package.json`). +3. Ensure all tests pass. + +### Publish to NPM +For each of these packages, simply run their automated publish script: + +**For Lit:** +```sh +cd renderers/lit +npm run publish:package +``` + +**For Angular:** +```sh +cd renderers/angular +npm run publish:package +``` + +**For Markdown-it:** +```sh +cd renderers/markdown/markdown-it +npm run publish:package +``` + +### How It Works (Explanations) + +**What happens during `npm run publish:package`?** +Before publishing, the script runs the necessary `build` command which processes the code. For Lit and Markdown-it, `prepare-publish.mjs` runs, and for Angular, `postprocess-build.mjs` runs. These scripts: +1. Copy `package.json`, `README.md`, and `LICENSE` to the `dist/` folder. +2. Read the `version` from `@a2ui/web_core`. +3. Update the `file:` dependency in the `dist/package.json` to the actual core version (e.g., `^0.8.0`). +4. Adjust exports and paths to be relative to `dist/`. +5. Remove any build scripts (`prepublishOnly`, `scripts`) so they don't interfere with the publish process. + +The `npm publish dist/` command then uploads only the contents of the `dist/` directory to the npm registry. + +--- + +## 🔖 Post-Publish +1. Tag the release (replace with actual version): + ```sh + git tag v0.8.0 + ``` +2. Push the tag: + ```sh + git push origin v0.8.0 + ``` +3. Create a GitHub Release mapping to the new tag. diff --git a/renderers/lit/package-lock.json b/renderers/lit/package-lock.json index a75bdfc75..c6a3e3f89 100644 --- a/renderers/lit/package-lock.json +++ b/renderers/lit/package-lock.json @@ -1,12 +1,12 @@ { "name": "@a2ui/lit", - "version": "0.8.1", + "version": "0.8.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@a2ui/lit", - "version": "0.8.1", + "version": "0.8.2", "license": "Apache-2.0", "dependencies": { "@a2ui/web_core": "file:../web_core", @@ -24,7 +24,7 @@ }, "../web_core": { "name": "@a2ui/web_core", - "version": "0.8.2", + "version": "0.8.0", "license": "Apache-2.0", "dependencies": { "zod": "^3.25.76", diff --git a/renderers/lit/package.json b/renderers/lit/package.json index 301275ffd..482b0b09e 100644 --- a/renderers/lit/package.json +++ b/renderers/lit/package.json @@ -1,9 +1,9 @@ { "name": "@a2ui/lit", - "version": "0.8.1", + "version": "0.8.3", "description": "A2UI Lit Library", - "main": "./dist/index.js", - "types": "./dist/index.d.ts", + "main": "./dist/src/index.js", + "types": "./dist/src/index.d.ts", "exports": { ".": { "types": "./dist/src/index.d.ts", @@ -20,7 +20,8 @@ }, "type": "module", "scripts": { - "prepack": "npm run build", + "prepublishOnly": "node -e \"if(!process.cwd().endsWith('dist')) { console.error('Error: This package must be published from the dist/ directory. Run `npm run build` then `npm publish dist/`'); process.exit(1); }\"", + "publish:package": "npm run build && node prepare-publish.mjs && npm publish dist/ --access public", "build": "wireit", "build:tsc": "wireit", "dev": "npm run serve --watch", diff --git a/renderers/lit/prepare-publish.mjs b/renderers/lit/prepare-publish.mjs index dc3941989..d33eae436 100644 --- a/renderers/lit/prepare-publish.mjs +++ b/renderers/lit/prepare-publish.mjs @@ -7,7 +7,7 @@ import { join } from 'path'; // 3. Adjusting paths in package.json (main, types, exports) to be relative to dist/ const dirname = import.meta.dirname; -const corePkgPath = join(dirname, '../core/package.json'); +const corePkgPath = join(dirname, '../web_core/package.json'); const litPkgPath = join(dirname, './package.json'); const distDir = join(dirname, './dist'); @@ -48,17 +48,28 @@ if (litPkg.exports) { } } +// Remove properties that should not be in the published package +delete litPkg.scripts; +delete litPkg.wireit; +delete litPkg.files; +delete litPkg.prepublishOnly; + // 5. Write to dist/package.json writeFileSync(join(distDir, 'package.json'), JSON.stringify(litPkg, null, 2)); // 6. Copy README and LICENSE -['README.md', 'LICENSE'].forEach(file => { - const src = join(dirname, file); - if (!existsSync(src)) { - throw new Error(`Missing required file for publishing: ${file}`); - } - copyFileSync(src, join(distDir, file)); -}); +const readmeSrc = join(dirname, 'README.md'); +const licenseSrc = join(dirname, '../../LICENSE'); + +if (!existsSync(readmeSrc)) { + throw new Error(`Missing required file for publishing: README.md`); +} +copyFileSync(readmeSrc, join(distDir, 'README.md')); + +if (!existsSync(licenseSrc)) { + throw new Error(`Missing required file for publishing: LICENSE`); +} +copyFileSync(licenseSrc, join(distDir, 'LICENSE')); console.log(`Prepared dist/package.json with @a2ui/web_core@${coreVersion}`); diff --git a/renderers/markdown/markdown-it/package-lock.json b/renderers/markdown/markdown-it/package-lock.json index 38bfbd750..b0219ab4d 100644 --- a/renderers/markdown/markdown-it/package-lock.json +++ b/renderers/markdown/markdown-it/package-lock.json @@ -29,7 +29,7 @@ }, "../../web_core": { "name": "@a2ui/web_core", - "version": "0.8.2", + "version": "0.8.0", "dev": true, "license": "Apache-2.0", "dependencies": { diff --git a/renderers/markdown/markdown-it/package.json b/renderers/markdown/markdown-it/package.json index 69de3d150..cd618c749 100644 --- a/renderers/markdown/markdown-it/package.json +++ b/renderers/markdown/markdown-it/package.json @@ -15,6 +15,8 @@ "license": "Apache-2.0", "author": "Google", "type": "module", + "main": "./dist/src/markdown.js", + "types": "./dist/src/markdown.d.ts", "exports": { ".": { "types": "./dist/src/markdown.d.ts", @@ -28,7 +30,8 @@ "build": "wireit", "format": "prettier --ignore-path ../.gitignore --write .", "format:check": "prettier --ignore-path ../.gitignore --check .", - "prepack": "npm run build", + "prepublishOnly": "node -e \"if(!process.cwd().endsWith('dist')) { console.error('Error: This package must be published from the dist/ directory. Run `npm run build` then `npm publish dist/`'); process.exit(1); }\"", + "publish:package": "npm run build && node prepare-publish.mjs && npm publish dist/ --access public", "test": "wireit" }, "dependencies": { diff --git a/renderers/markdown/markdown-it/prepare-publish.mjs b/renderers/markdown/markdown-it/prepare-publish.mjs new file mode 100644 index 000000000..4f70fd1b6 --- /dev/null +++ b/renderers/markdown/markdown-it/prepare-publish.mjs @@ -0,0 +1,85 @@ +import { readFileSync, writeFileSync, copyFileSync, mkdirSync, existsSync } from 'fs'; +import { join } from 'path'; + +// This script prepares the markdown-it package for publishing by: +// 1. Copying package.json to dist/ +// 2. Updating @a2ui/web_core dependency from 'file:...' to the actual version +// 3. Adjusting paths in package.json (main, types, exports) to be relative to dist/ + +const dirname = import.meta.dirname; +const corePkgPath = join(dirname, '../../web_core/package.json'); +const pkgPath = join(dirname, './package.json'); +const distDir = join(dirname, './dist'); + +if (!existsSync(distDir)) { + mkdirSync(distDir, { recursive: true }); +} + +// 1. Get Core Version +const corePkg = JSON.parse(readFileSync(corePkgPath, 'utf8')); +const coreVersion = corePkg.version; +if (!coreVersion) throw new Error('Cannot determine @a2ui/web_core version'); + +// 2. Read Package +const pkg = JSON.parse(readFileSync(pkgPath, 'utf8')); + +// 3. Update Dependency +if (pkg.peerDependencies && pkg.peerDependencies['@a2ui/web_core']) { + pkg.peerDependencies['@a2ui/web_core'] = '^' + coreVersion; +} else { + console.warn('Warning: @a2ui/web_core not found in peerDependencies.'); +} +if (pkg.devDependencies && pkg.devDependencies['@a2ui/web_core']) { + // We can just remove devDependencies for the published package, or update it + pkg.devDependencies['@a2ui/web_core'] = '^' + coreVersion; +} + +// 4. Adjust Paths for Dist +pkg.main = adjustPath(pkg.main); +pkg.types = adjustPath(pkg.types); + +if (pkg.exports) { + for (const key in pkg.exports) { + const exp = pkg.exports[key]; + if (typeof exp === 'string') { + pkg.exports[key] = adjustPath(exp); + } else { + if (exp.types) exp.types = adjustPath(exp.types); + if (exp.default) exp.default = adjustPath(exp.default); + if (exp.import) exp.import = adjustPath(exp.import); + if (exp.require) exp.require = adjustPath(exp.require); + } + } +} + +// Remove files and wireit properties since we are publishing the dist folder directly +delete pkg.files; +delete pkg.wireit; +delete pkg.scripts; + +// 5. Write to dist/package.json +writeFileSync(join(distDir, 'package.json'), JSON.stringify(pkg, null, 2)); + +// 6. Copy README and LICENSE +// LICENSE is in the root directory for this package structure, or we can copy from parent +const licenseSrc = join(dirname, '../../../LICENSE'); +const readmeSrc = join(dirname, 'README.md'); + +if (existsSync(readmeSrc)) { + copyFileSync(readmeSrc, join(distDir, 'README.md')); +} +if (existsSync(licenseSrc)) { + copyFileSync(licenseSrc, join(distDir, 'LICENSE')); +} else { + console.warn("Could not find LICENSE at " + licenseSrc); +} + +console.log(`Prepared dist/package.json with @a2ui/web_core@${coreVersion}`); + +// Utility function to adjust the paths of the built files (dist/src/*) to (src/*) +function adjustPath(p) { + if (p && p.startsWith('./dist/')) { + return './' + p.substring(7); // Remove ./dist/ + } + return p; +} diff --git a/renderers/markdown/markdown-it/src/markdown.test.ts b/renderers/markdown/markdown-it/src/markdown.test.ts index 4378f8f95..7ea097b89 100644 --- a/renderers/markdown/markdown-it/src/markdown.test.ts +++ b/renderers/markdown/markdown-it/src/markdown.test.ts @@ -1,3 +1,19 @@ +/* + Copyright 2025 Google LLC + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + import { JSDOM } from 'jsdom'; // Provide a jsdom window for DOMPurify in the Node test environment. diff --git a/renderers/web_core/.gitignore b/renderers/web_core/.gitignore new file mode 100644 index 000000000..f346bdf1a --- /dev/null +++ b/renderers/web_core/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +.wireit/ +.npmrc diff --git a/renderers/web_core/README.md b/renderers/web_core/README.md new file mode 100644 index 000000000..58720d131 --- /dev/null +++ b/renderers/web_core/README.md @@ -0,0 +1,13 @@ +# @a2ui/web_core + +This is the Core Library for A2UI web renderers. + +## Installation + +```sh +npm install @a2ui/web_core +``` + +## Usage + +Please refer to the A2UI documentation for usage instructions. diff --git a/renderers/web_core/package-lock.json b/renderers/web_core/package-lock.json index 453b29bc3..86e424840 100644 --- a/renderers/web_core/package-lock.json +++ b/renderers/web_core/package-lock.json @@ -1,12 +1,12 @@ { "name": "@a2ui/web_core", - "version": "0.8.2", + "version": "0.8.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@a2ui/web_core", - "version": "0.8.2", + "version": "0.8.0", "license": "Apache-2.0", "dependencies": { "zod": "^3.25.76", diff --git a/renderers/web_core/package.json b/renderers/web_core/package.json index 3b8b3f50a..3c8fcd097 100644 --- a/renderers/web_core/package.json +++ b/renderers/web_core/package.json @@ -1,6 +1,6 @@ { "name": "@a2ui/web_core", - "version": "0.8.2", + "version": "0.8.0", "description": "A2UI Core Library", "main": "./dist/src/v0_8/index.js", "types": "./dist/src/v0_8/index.d.ts", @@ -31,6 +31,10 @@ }, "type": "module", "sideEffects": false, + "files": [ + "dist", + "src" + ], "scripts": { "prepack": "npm run build", "build": "wireit",