Skip to content
Merged
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
3 changes: 3 additions & 0 deletions .github/workflows/javascript-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,8 @@ jobs:
export NODE_OPTIONS="--max-old-space-size=4096"
yarn ci

- name: Verify packed packages hold no specifications
run: yarn verify-packages

- name: Build Storybook
run: yarn build-storybook
3 changes: 2 additions & 1 deletion Source/JavaScript/blueprint.components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"files": [
"dist",
"**/*.ts",
"**/*.tsx"
"**/*.tsx",
"!**/for_*/**"
],
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down
3 changes: 2 additions & 1 deletion Source/JavaScript/blueprint.default/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"dist",
"**/*.ts",
"**/*.tsx",
"shell/layout.css"
"shell/layout.css",
"!**/for_*/**"
],
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down
3 changes: 2 additions & 1 deletion Source/JavaScript/components/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
"dist",
"**/*.ts",
"**/*.tsx",
"**/*.css"
"**/*.css",
"!**/for_*/**"
],
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down
3 changes: 2 additions & 1 deletion Source/JavaScript/engine/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
},
"files": [
"dist",
"**/*.ts"
"**/*.ts",
"!**/for_*/**"
],
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down
3 changes: 2 additions & 1 deletion Source/JavaScript/model/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
},
"files": [
"dist",
"**/*.ts"
"**/*.ts",
"!**/for_*/**"
],
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down
3 changes: 2 additions & 1 deletion Source/JavaScript/primereact/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
"dist",
"**/*.ts",
"**/*.tsx",
"**/*.css"
"**/*.css",
"!**/for_*/**"
],
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down
3 changes: 2 additions & 1 deletion Source/JavaScript/react/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
"files": [
"dist",
"**/*.ts",
"**/*.tsx"
"**/*.tsx",
"!**/for_*/**"
],
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down
3 changes: 2 additions & 1 deletion Source/JavaScript/tailwind/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@
"files": [
"dist",
"**/*.ts",
"**/*.tsx"
"**/*.tsx",
"!**/for_*/**"
],
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"test": "node ./run-task-on-workspaces.js test",
"ci": "node ./run-task-on-workspaces.js ci",
"build-storybook": "node ./run-task-on-workspaces.js build-storybook",
"verify-packages": "node ./verify-package-contents.js",
"publish-version": "node ./run-task-on-workspaces.js publish-version $*",
"up": "node ./run-task-on-workspaces.js up",
"postinstall": "node ./link-eslint-typescript.js"
Expand Down
83 changes: 83 additions & 0 deletions verify-package-contents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/usr/bin/env node
/* eslint-disable header/header */

// Packs every public workspace package (dry run) and fails when the archive would carry specifications.
//
// Specifications live next to the code they describe, in `for_*` folders. `tsc -b` type-checks and emits
// them with everything else, so the build output contains them and only the `files` list in each
// package.json keeps them out of the archive. This checks the real packed file list, so a package that
// drops or loosens that exclusion fails here instead of shipping compiled specifications.
//
// Run it after `yarn build`, from the repository root:
//
// node ./verify-package-contents.js

const path = require('path');
const fs = require('fs');
const spawn = require('child_process').spawnSync;
const glob = require('glob').sync;

const specificationFolder = /(^|\/)for_[^/]+\//;
const testRunnerImport = /(?:\bfrom\s*|\brequire\(\s*|\bimport\(\s*)['"](?:vitest|chai|chai-as-promised|sinon|sinon-chai|@testing-library\/[^'"]+)['"]/;
const runtimeScript = /\.(?:js|cjs|mjs)$/;

function packedFiles(packageDirectory) {
const result = spawn('npm', ['pack', '--dry-run', '--json', '--ignore-scripts'], { cwd: packageDirectory, encoding: 'utf8' });
if (result.status !== 0) {
throw new Error(`npm pack failed in '${packageDirectory}':\n${result.stderr}`);
}

// npm 10 runs the package's `prepare` script even with --ignore-scripts, and that script writes its own
// progress to stdout ahead of the JSON report, so parse from the line where the report starts.
// npm reports an array of packed packages; newer versions key the same entries by package name.
const start = result.stdout.search(/^[[{]\s*$/m);
if (start < 0) {
throw new Error(`npm pack reported no packed files in '${packageDirectory}':\n${result.stdout}`);
}

const report = JSON.parse(result.stdout.slice(start));
const entry = Array.isArray(report) ? report[0] : Object.values(report)[0];
return entry.files.map(_ => _.path);
}

const packageFiles = glob('Source/JavaScript/*/package.json', { cwd: process.cwd(), ignore: ['**/dist/**', '**/node_modules/**'] });
const problems = [];
let packages = 0;

for (const packageFile of packageFiles.sort()) {
const packageDirectory = path.dirname(packageFile);
const packageJson = JSON.parse(fs.readFileSync(packageFile, 'utf8'));
if (packageJson.private === true) {
continue;
}

packages++;
const files = packedFiles(packageDirectory);
if (!files.some(_ => _.startsWith('dist/'))) {
problems.push(`${packageJson.name}: the archive has no 'dist/' output - run the build before verifying`);
continue;
}

for (const file of files) {
if (specificationFolder.test(file)) {
problems.push(`${packageJson.name}: '${file}' is a specification and must not be packed`);
} else if (runtimeScript.test(file) && testRunnerImport.test(fs.readFileSync(path.join(packageDirectory, file), 'utf8'))) {
problems.push(`${packageJson.name}: '${file}' imports a test runner`);
}
}

console.log(`Verified '${packageJson.name}': ${files.length} packed files`);
}

if (packages === 0) {
console.log('No public packages found under Source/JavaScript');
process.exit(1);
}

if (problems.length > 0) {
console.log(`\n${problems.length} problem(s) in packed packages:`);
problems.forEach(_ => console.log(` ${_}`));
process.exit(1);
}

console.log(`\nNo specifications or test-runner imports in ${packages} packed package(s)`);
Loading