diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index df2a81b..81e68a4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -32,12 +32,15 @@ jobs:
debug-${{ runner.os }}-
- name: Run tests
run: cargo test --verbose
+
- name: Install wasm-pack
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
- - run: wasm-pack build --target nodejs
+ - name: Install wasm-opt
+ run: cargo install wasm-opt
- uses: actions/setup-node@v3
with:
node-version: 16
cache: 'npm'
- - run: npm ci
+ - run: ./build.sh
+
- run: npm test
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index b04b8d0..d95286e 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -18,7 +18,7 @@ jobs:
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
# remove scope or update it once we decide exactly where we want to deploy this thing
- - run: wasm-pack build --target nodejs
+ - run: npm run build
- uses: actions/setup-node@v3
with:
node-version: 20
diff --git a/.gitignore b/.gitignore
index 50b6338..3274f69 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
+pkg/
/target
-/node_modules
\ No newline at end of file
+/node_modules
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index c80c587..e7c6dcc 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -18,7 +18,7 @@ Tests:
Build wasm package:
-`wasm-pack build --target=nodejs`
+`npm run build`
which will output your wasm package in `./pkg`
@@ -36,4 +36,4 @@ and replace:
```
$1 = { path = "../swc/crates/$1"
-```
\ No newline at end of file
+```
diff --git a/Cargo.lock b/Cargo.lock
index ca00b40..ff2534d 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -276,7 +276,7 @@ dependencies = [
[[package]]
name = "content-tag"
-version = "1.1.1"
+version = "1.1.2"
dependencies = [
"base64 0.21.4",
"difference",
diff --git a/Cargo.toml b/Cargo.toml
index 5e8ff19..4b6249b 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -9,6 +9,11 @@ edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
+[profile.release]
+lto = true
+opt-level = 'z'
+codegen-units = 1
+
[dependencies]
swc_common = { git = "https://github.com/ef4/swc.git", branch = "content-tag", features=["tty-emitter"] }
swc = { git = "https://github.com/ef4/swc.git", branch = "content-tag" }
diff --git a/README.md b/README.md
index aae6259..c380291 100644
--- a/README.md
+++ b/README.md
@@ -12,10 +12,33 @@ npm install content-tag
## Usage
+### Node (CommonJS)
+
```js
let { Preprocessor } = require('content-tag');
let p = new Preprocessor();
let output = p.process('Hi');
+
+console.log(output);
+```
+
+### Node (ESM)
+
+```js
+import { Preprocessor } from 'content-tag';
+let p = new Preprocessor();
+let output = p.process('Hi');
+
+console.log(output);
+```
+
+### Browser (ESM)
+
+```js
+import { createPreprocessor } from 'content-tag/standalone';
+let p = await createPreprocessor();
+let output = p.process('Hi');
+
console.log(output);
```
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..ab9faea
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,26 @@
+#!/bin/bash
+
+# we need npm packages for the post-wasm phase
+npm install
+
+rm -rf pkg
+
+# wasm-pack knows to use wasm-opt, when present
+# NOTE: wasm-pack does not support multi-target building
+# so we'll build twice, and then tweak package.json
+# "exports" to point at the correct build depending on node or browser
+wasm-pack build --target web --out-dir pkg/standalone --weak-refs --no-pack --release
+wasm-pack build --target nodejs --out-dir pkg/node --weak-refs --no-pack --release
+
+# Rename the node js file to cjs, because we emit type=module
+mv pkg/node/content_tag.js pkg/node/content_tag.cjs
+
+# generate the rest of the package for npm, since
+# we disabled package.json generation above with --no-pack.
+# this needs to be done because we're targeting
+# both browser and node, which wasm-packg doesn't natively support.
+node ./build/post-wasm-build.mjs
+
+# ---------
+cp LICENSE pkg/LICENSE
+cp README.md pkg/README.md
diff --git a/build/post-wasm-build.mjs b/build/post-wasm-build.mjs
new file mode 100644
index 0000000..6950c6a
--- /dev/null
+++ b/build/post-wasm-build.mjs
@@ -0,0 +1,47 @@
+// This post-wasm-build.js script is called from build.sh
+import fs from "node:fs/promises";
+import path from "node:path";
+import url from "node:url";
+import toml from "toml";
+
+let cargo = await fs.readFile("Cargo.toml", "utf8");
+let config = toml.parse(cargo);
+
+const manifest = {
+ name: config.package.name,
+ description: config.package.description,
+ version: config.package.version,
+ license: config.package.license,
+ repository: {
+ type: "git",
+ url: "https://github.com/embroider-build/content-tag",
+ },
+ files: ["standalone", "node"],
+ type: "module",
+ exports: {
+ ".": {
+ types: "./node/content_tag.d.ts",
+ default: "./node/content_tag.cjs",
+ },
+ "./standalone": {
+ types: "./standalone/standalone.d.ts",
+ import: "./standalone/standalone.js",
+ },
+ },
+};
+
+const content = JSON.stringify(manifest, null, 2);
+
+const here = url.fileURLToPath(new URL(".", import.meta.url));
+const root = path.join(here, "..");
+const output = path.join(root, "pkg");
+
+await fs.writeFile(path.join(output, "package.json"), content);
+await fs.copyFile(
+ path.join(here, "src/standalone.js"),
+ path.join(output, "standalone/standalone.js")
+);
+await fs.copyFile(
+ path.join(here, "src/standalone.d.ts"),
+ path.join(output, "standalone/standalone.d.ts")
+);
diff --git a/build/src/standalone.d.ts b/build/src/standalone.d.ts
new file mode 100644
index 0000000..c450256
--- /dev/null
+++ b/build/src/standalone.d.ts
@@ -0,0 +1,5 @@
+import type { Preprocessor } from './content_tag';
+
+export * from "./content_tag";
+
+export function createPreprocessor(): Promise;
diff --git a/build/src/standalone.js b/build/src/standalone.js
new file mode 100644
index 0000000..8260da0
--- /dev/null
+++ b/build/src/standalone.js
@@ -0,0 +1,12 @@
+export * from "./content_tag.js";
+
+import init, { Preprocessor } from "./content_tag.js";
+
+export async function createPreprocessor() {
+ // This no-ops if it's already ran
+ await init();
+
+ let processor = new Preprocessor();
+
+ return processor;
+}
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..ccc9326
--- /dev/null
+++ b/index.html
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/package-lock.json b/package-lock.json
index 5ec38f5..c63e8a9 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,9 +11,11 @@
"@release-it/bumper": "^5.1.0",
"chai": "^4.3.7",
"code-equality-assertions": "github:mansona/code-equality-assertions#add-chai-build",
+ "content-tag": "file:./pkg",
"eslint": "^8.44.0",
"mocha": "^10.2.0",
- "release-it": "^16.2.1"
+ "release-it": "^16.2.1",
+ "toml": "^3.0.0"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -2086,6 +2088,10 @@
"url": "https://github.com/yeoman/configstore?sponsor=1"
}
},
+ "node_modules/content-tag": {
+ "resolved": "pkg",
+ "link": true
+ },
"node_modules/convert-source-map": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
@@ -7734,6 +7740,12 @@
"node": ">=8.0"
}
},
+ "node_modules/toml": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/toml/-/toml-3.0.0.tgz",
+ "integrity": "sha512-y/mWCZinnvxjTKYhJ+pYxwD0mRLVvOtdS2Awbgxln6iEnt4rk0yBxeSBHkGJcPucRiG0e55mwWp+g/05rsrd6w==",
+ "dev": true
+ },
"node_modules/tr46": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
@@ -8354,6 +8366,12 @@
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
+ },
+ "pkg": {
+ "name": "content-tag",
+ "version": "1.1.2",
+ "dev": true,
+ "license": "MIT"
}
}
}
diff --git a/package.json b/package.json
index 7e4b8b5..a1fd455 100644
--- a/package.json
+++ b/package.json
@@ -5,6 +5,8 @@
"url": "git@github.com:embroider-build/content-tag.git"
},
"scripts": {
+ "web": "npx html-pages . --no-cache",
+ "build": "./build.sh",
"test": "mocha"
},
"devDependencies": {
@@ -12,8 +14,10 @@
"@release-it/bumper": "^5.1.0",
"chai": "^4.3.7",
"code-equality-assertions": "github:mansona/code-equality-assertions#add-chai-build",
+ "content-tag": "file:./pkg",
"eslint": "^8.44.0",
"mocha": "^10.2.0",
+ "toml": "^3.0.0",
"release-it": "^16.2.1"
},
"publishConfig": {
diff --git a/test/node-api-esm.mjs b/test/node-api-esm.mjs
new file mode 100644
index 0000000..55a3c02
--- /dev/null
+++ b/test/node-api-esm.mjs
@@ -0,0 +1,65 @@
+import chai from "chai";
+import { codeEquality } from "code-equality-assertions/chai";
+
+chai.use(codeEquality);
+
+const { expect } = chai;
+
+import { Preprocessor } from "content-tag";
+const p = new Preprocessor();
+
+describe("Node ESM", function () {
+ it("works for a basic example", function () {
+ let output = p.process("Hi");
+
+ expect(output).to
+ .equalCode(`import { template } from "@ember/template-compiler";
+export default template(\`Hi\`, {
+ eval () {
+ return eval(arguments[0]);
+ }
+});`);
+ });
+
+ it("Emits parse errors with anonymous file", function () {
+ expect(function () {
+ p.process(`const thing = "face";
+ Hi`);
+ }).to.throw(`Parse Error at :2:15: 2:15`);
+ });
+
+ it("Emits parse errors with real file", function () {
+ expect(function () {
+ p.process(
+ `const thing = "face";
+ Hi`,
+ "path/to/my/component.gjs"
+ );
+ }).to.throw(`Parse Error at path/to/my/component.gjs:2:15: 2:15`);
+ });
+
+ it("Offers source_code snippet on parse errors", function () {
+ let parseError;
+ try {
+ p.process(`class {`);
+ } catch (err) {
+ parseError = err;
+ }
+ expect(parseError)
+ .to.have.property("source_code")
+ .matches(/Expected ident.*class \{/s);
+ });
+
+ it("Offers source_code_color snippet on parse errors", function () {
+ let parseError;
+ try {
+ p.process(`class {`);
+ } catch (err) {
+ parseError = err;
+ }
+ // eslint-disable-next-line no-control-regex
+ expect(parseError)
+ .to.have.property("source_code_color")
+ .matches(/Expected ident.*[\u001b].*class \{/s);
+ });
+});
diff --git a/test/node-api.js b/test/node-api.js
index 76b9015..0b8c013 100644
--- a/test/node-api.js
+++ b/test/node-api.js
@@ -1,4 +1,4 @@
-const { Preprocessor } = require("../pkg");
+const { Preprocessor } = require("content-tag");
const chai = require("chai");
const { codeEquality } = require("code-equality-assertions/chai");
@@ -217,10 +217,8 @@ describe("process", function () {
}
};`
);
-
});
-
it("Emits parse errors with anonymous file", function () {
expect(function () {
p.process(`const thing = "face";