diff --git a/CHANGELOG.md b/CHANGELOG.md index bc17d7a59b42..35501a087fb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Expose `ccf:http::parse_accept_header()` and `ccf::http::AcceptHeaderField` (#6706). - Added `ccf::cose::AbstractCOSESignaturesConfig` subsystem to expose COSE signature configuration to application handlers (#6707). +- Package `build_bundle.ts` under `npx ccf-build-bundle` to allow javascript users to build a ccf schema bundle (#6704). ## [6.0.0-dev9] diff --git a/doc/build_apps/js_app_bundle.rst b/doc/build_apps/js_app_bundle.rst index 7100ac80f3b4..d1eb664f39e5 100644 --- a/doc/build_apps/js_app_bundle.rst +++ b/doc/build_apps/js_app_bundle.rst @@ -338,8 +338,7 @@ The key fields are: Once :ref:`submitted and accepted `, a ``set_js_app`` proposal atomically (re-)deploys the complete JavaScript application. Any existing application endpoints and JavaScript modules are removed. -If you are using ``npm`` or similar to build your app it may make sense to convert your app into a proposal-ready JSON bundle during packaging. -For an example of how this could be done, see :ccf_repo:`tests/npm-app/build_bundle.js` from one of CCF's test applications, called by ``npm build`` from the corresponding :ccf_repo:`tests/npm-app/package.json`. +If you are using ``npm`` to build your app, we package a `ccf-build-bundle` script alongside `ccf-app`. This can be run using `npx --package @microsoft/ccf-app ccf-build-bundle path/to/root/of/app` to package the `app.json` and all javascript modules under `src` into a proposal-ready JSON bundle. Bytecode cache ~~~~~~~~~~~~~~ diff --git a/js/ccf-app/.gitignore b/js/ccf-app/.gitignore index 92780ed4fa90..f0ceb8748d0e 100644 --- a/js/ccf-app/.gitignore +++ b/js/ccf-app/.gitignore @@ -1,4 +1,5 @@ /*.tgz /*.d.ts /*.js -/html \ No newline at end of file +/html +/scripts \ No newline at end of file diff --git a/js/ccf-app/package.json b/js/ccf-app/package.json index e0aff0851229..dddf43ea99ca 100644 --- a/js/ccf-app/package.json +++ b/js/ccf-app/package.json @@ -31,5 +31,8 @@ "ts-node": "^10.4.0", "typedoc": "^0.27.0", "typescript": "^5.7.2" + }, + "bin": { + "ccf-build-bundle": "scripts/build_bundle.js" } } diff --git a/js/ccf-app/src/scripts/build_bundle.ts b/js/ccf-app/src/scripts/build_bundle.ts new file mode 100644 index 000000000000..ae84db5447fa --- /dev/null +++ b/js/ccf-app/src/scripts/build_bundle.ts @@ -0,0 +1,81 @@ +#!/usr/bin/env node + +import { + readdirSync, + statSync, + readFileSync, + writeFileSync, + existsSync, +} from "fs"; +import { join, resolve, sep } from "path"; + +function getAllFiles(dirPath: string): string[] { + const toSearch = [dirPath]; + const agg = []; + + for (const filePath of toSearch) { + if (statSync(filePath).isDirectory()) { + for (const subfile of readdirSync(filePath)) { + toSearch.push(join(filePath, subfile)); + } + } else { + agg.push(filePath); + } + } + return agg; +} + +function removePrefix(s: string, prefix: string): string { + if (s.startsWith(prefix)) { + return s.slice(prefix.length).split(sep).join(sep); + } + console.log("Warn: tried to remove invalid prefix", s, prefix); + return s; +} + +const args = process.argv.slice(2); + +if (args.length < 1) { + console.log("Usage: build_bundle "); + process.exit(1); +} + +function assertFileExists(path: string) { + if (!existsSync(path)) { + console.log("File not found: %s", path); + process.exit(1); + } +} + +const argRootDirPath = args[0]; +assertFileExists(argRootDirPath); +const rootDirPath = resolve(argRootDirPath); +const metadataPath = join(rootDirPath, "app.json"); +assertFileExists(metadataPath); +const srcDirPath = join(rootDirPath, "src"); +assertFileExists(srcDirPath); + +const metadata = JSON.parse(readFileSync(metadataPath, "utf-8")); +const allFiles = getAllFiles(srcDirPath); + +// The trailing / is included so that it is trimmed in removePrefix. +// This produces "foo/bar.js" rather than "/foo/bar.js" +const toTrim = srcDirPath + "/"; + +const modules = allFiles.map(function (filePath) { + return { + name: removePrefix(filePath, toTrim), + module: readFileSync(filePath, "utf-8"), + }; +}); + +const bundlePath = join(args[0], "bundle.json"); +const bundle = { + metadata: metadata, + modules: modules, +}; + +console.log( + `Writing bundle containing ${modules.length} modules to ${bundlePath}`, +); +writeFileSync(bundlePath, JSON.stringify(bundle)); diff --git a/tests/npm-app/build_bundle.js b/tests/npm-app/build_bundle.js deleted file mode 100644 index 83ad2c3e22f6..000000000000 --- a/tests/npm-app/build_bundle.js +++ /dev/null @@ -1,53 +0,0 @@ -import { readdirSync, statSync, readFileSync, writeFileSync } from "fs"; -import { join, posix, sep } from "path"; - -const args = process.argv.slice(2); - -const getAllFiles = function (dirPath, arrayOfFiles) { - arrayOfFiles = arrayOfFiles || []; - - const files = readdirSync(dirPath); - for (const file of files) { - const filePath = join(dirPath, file); - if (statSync(filePath).isDirectory()) { - arrayOfFiles = getAllFiles(filePath, arrayOfFiles); - } else { - arrayOfFiles.push(filePath); - } - } - - return arrayOfFiles; -}; - -const removePrefix = function (s, prefix) { - return s.substr(prefix.length).split(sep).join(posix.sep); -}; - -const rootDir = args[0]; - -const metadataPath = join(rootDir, "app.json"); -const metadata = JSON.parse(readFileSync(metadataPath, "utf-8")); - -const srcDir = join(rootDir, "src"); -const allFiles = getAllFiles(srcDir); - -// The trailing / is included so that it is trimmed in removePrefix. -// This produces "foo/bar.js" rather than "/foo/bar.js" -const toTrim = srcDir + "/"; - -const modules = allFiles.map(function (filePath) { - return { - name: removePrefix(filePath, toTrim), - module: readFileSync(filePath, "utf-8"), - }; -}); - -const bundlePath = join(args[0], "bundle.json"); -const bundle = { - metadata: metadata, - modules: modules, -}; -console.log( - `Writing bundle containing ${modules.length} modules to ${bundlePath}`, -); -writeFileSync(bundlePath, JSON.stringify(bundle)); diff --git a/tests/npm-app/package.json b/tests/npm-app/package.json index b0cc8ca2fa33..35577a12540b 100644 --- a/tests/npm-app/package.json +++ b/tests/npm-app/package.json @@ -1,8 +1,8 @@ { "private": true, "scripts": { - "build": "del-cli -f dist/ && rollup --config && cp app.json dist/ && node build_bundle.js dist/", - "bundle": "node build_bundle.js dist", + "build": "del-cli -f dist/ && rollup --config && cp app.json dist/ && npx ccf-build-bundle dist", + "bundle": "npx ccf-build-bundle dist", "test": "node --version" }, "type": "module",