Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion renderers/angular/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 6 additions & 0 deletions renderers/angular/postprocess-build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,12 @@ if (!packageJson.dependencies['@a2ui/web_core']) {
}

packageJson.dependencies['@a2ui/web_core'] = '^' + coreVersion;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should stop post-processing the web_core dependency, and instead, take a dependency on the version of the package that's published on npm. That way, these build script could be simplified quite a lot! WDYT?

Then, we'd need some instructions on how to develop locally to be able to modify multiple packages at once, with workspaces, or npm link (?)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my 2¢ is a npm link workflow is probably the best way to approach this, whenever we document that.


// 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'));
Expand Down
124 changes: 124 additions & 0 deletions renderers/docs/web_publishing.md
Original file line number Diff line number Diff line change
@@ -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.
6 changes: 3 additions & 3 deletions renderers/lit/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 5 additions & 4 deletions renderers/lit/package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand All @@ -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",
Expand Down
27 changes: 19 additions & 8 deletions renderers/lit/prepare-publish.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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');

Expand Down Expand Up @@ -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}`);

Expand Down
2 changes: 1 addition & 1 deletion renderers/markdown/markdown-it/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion renderers/markdown/markdown-it/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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": {
Expand Down
85 changes: 85 additions & 0 deletions renderers/markdown/markdown-it/prepare-publish.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { readFileSync, writeFileSync, copyFileSync, mkdirSync, existsSync } from 'fs';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copyright?

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');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This package is going to be fairly stable, and it most likely won't need a lot of changes to web_core. Can you please update the package.json to take a dependency on the published version of web_core? Maybe we can remove most of this file?

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;
}
16 changes: 16 additions & 0 deletions renderers/markdown/markdown-it/src/markdown.test.ts
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
4 changes: 4 additions & 0 deletions renderers/web_core/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
node_modules/
dist/
.wireit/
.npmrc
13 changes: 13 additions & 0 deletions renderers/web_core/README.md
Original file line number Diff line number Diff line change
@@ -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.
Loading
Loading