Skip to content

Commit 6f1b794

Browse files
authored
chore(scripts): update license check to be more flexible @W-19066903 (#5422)
* chore(scripts): convert license script to ESM * chore(scripts): change license script from .js to .mjs * chore(scripts): make license file look in more places @parse5/tools moved from root to template-compiler and there's additional ESM vs CJS nonsense to deal with * chore: add a comment
1 parent a5b0209 commit 6f1b794

File tree

6 files changed

+126
-104
lines changed

6 files changed

+126
-104
lines changed

.git2gus/config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"productTag": "a1aB0000000g309IAA",
3-
"defaultBuild": "256",
3+
"defaultBuild": "258",
44
"hideWorkItemUrl": true
55
}

.github/workflows/unit.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ jobs:
5151
- name: Check package.json integrity
5252
run: node ./scripts/tasks/check-and-rewrite-package-json.js --test
5353
- name: Check licenses are up to date
54-
run: node ./scripts/tasks/generate-license-files.js --test
54+
run: node ./scripts/tasks/generate-license-files.mjs --test
5555
- name: Verify @lwc/shared is tree-shakable
5656
run: node ./scripts/tasks/verify-treeshakable.mjs ./packages/@lwc/shared/dist/index.js
5757
- name: Verify that dependencies are declared

.husky/pre-push

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
set -e
22
node ./scripts/tasks/check-and-rewrite-package-json.js --test
3-
node ./scripts/tasks/generate-license-files.js --test
3+
node ./scripts/tasks/generate-license-files.mjs --test
44
node ./scripts/tasks/verify-treeshakable.mjs ./packages/@lwc/shared/dist/index.js
55
node ./scripts/tasks/check-imports-are-declared-dependencies.js

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@
8181
"*.{js,mjs,ts,only,skip}": "eslint --cache",
8282
"*.{css,js,json,md,mjs,ts,yaml,yml}": "prettier --check",
8383
"{packages/**/package.json,scripts/tasks/check-and-rewrite-package-json.js}": "node ./scripts/tasks/check-and-rewrite-package-json.js --test",
84-
"{LICENSE-CORE.md,**/LICENSE.md,yarn.lock,scripts/tasks/generate-license-files.js,scripts/shared/bundled-dependencies.js}": "node ./scripts/tasks/generate-license-files.js --test"
84+
"{LICENSE-CORE.md,**/LICENSE.md,yarn.lock,scripts/tasks/generate-license-files.mjs,scripts/shared/bundled-dependencies.js}": "node ./scripts/tasks/generate-license-files.mjs --test"
8585
},
8686
"workspaces": [
8787
"packages/@lwc/*",

scripts/tasks/generate-license-files.js

Lines changed: 0 additions & 100 deletions
This file was deleted.
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import path from 'node:path';
2+
import { readFile, writeFile, stat, readdir } from 'node:fs/promises';
3+
import { createRequire } from 'node:module';
4+
import { fileURLToPath } from 'node:url';
5+
import prettier from 'prettier';
6+
import { BUNDLED_DEPENDENCIES } from '../shared/bundled-dependencies.js';
7+
8+
// We're using ESM here, but some packages still only export CJS, so we also need `require`
9+
const require = createRequire(import.meta.url);
10+
11+
const atLwcPackages = (await readdir('packages/@lwc'))
12+
// skip dotfiles like .DS_Store
13+
.filter((_) => !_.startsWith('.'))
14+
.map((_) => `@lwc/${_}`);
15+
16+
// Generate our LICENSE files for each package, including any bundled dependencies
17+
// This is modeled after how Rollup does it:
18+
// https://github.com/rollup/rollup/blob/0b665c3/build-plugins/generate-license-file.ts
19+
20+
// Async fs.existsSync
21+
async function exists(filename) {
22+
try {
23+
await stat(filename);
24+
return true;
25+
} catch (_err) {
26+
return false;
27+
}
28+
}
29+
30+
/**
31+
* Tries `require.resolve` with additional paths (`packages/@lwc/___/node_modules`)
32+
* and `import.meta.resolve` (unmodified) to find a package's entrypoint.
33+
*/
34+
function tryResolve(specifier) {
35+
try {
36+
// As far as I can tell, there's no way to modify the `import` lookup paths
37+
return fileURLToPath(import.meta.resolve(specifier));
38+
} catch (err) {
39+
// We expect to see missing packages, but throw other errors
40+
if (err.code !== 'ERR_MODULE_NOT_FOUND') {
41+
throw err;
42+
}
43+
}
44+
// `require.resolve` accepts a second parameter of additional places to look
45+
return require.resolve(specifier, {
46+
paths: atLwcPackages.map((pkg) => path.join('packages', pkg, 'node_modules')),
47+
});
48+
}
49+
50+
/**
51+
* Finds a dependency in our monorepo.
52+
* @param {string} specifier - package name to find
53+
*/
54+
function findPackageDirectory(specifier) {
55+
const resolved = tryResolve(specifier);
56+
// An import can resolve to a nested directory, e.g. dist/index.js. We want the package
57+
// root, which will always be the last node_modules/${specifier}.
58+
const lookup = path.join('/node_modules', specifier);
59+
return resolved.slice(0, resolved.lastIndexOf(lookup) + lookup.length);
60+
}
61+
62+
async function findLicenseText(depName) {
63+
// Iterate through possible names for the license file
64+
const names = ['LICENSE', 'LICENSE.md', 'LICENSE.txt'];
65+
66+
const resolvedDepPath = findPackageDirectory(depName);
67+
68+
for (const name of names) {
69+
const fullFilePath = path.join(resolvedDepPath, name);
70+
if (await exists(fullFilePath)) {
71+
return (await readFile(fullFilePath, 'utf-8')).trim();
72+
}
73+
}
74+
75+
// Get the license from the package.json if we can't find it elsewhere
76+
const { license, version } = JSON.parse(
77+
await readFile(path.join(resolvedDepPath, 'package.json'), 'utf-8')
78+
);
79+
80+
return `${license} license defined in package.json in v${version}.`;
81+
}
82+
83+
const coreLicense = await readFile('LICENSE-CORE.md', 'utf-8');
84+
85+
const bundledLicenses = await Promise.all(
86+
BUNDLED_DEPENDENCIES.map(async (depName) => {
87+
return `## ${depName}\n\n` + (await findLicenseText(depName));
88+
})
89+
);
90+
const newLicense =
91+
`# LWC core license\n\n${coreLicense}\n# Licenses of bundled dependencies\n\n${bundledLicenses.join(
92+
'\n'
93+
)}`.trim() + '\n';
94+
95+
const formattedLicense = await prettier.format(newLicense, {
96+
parser: 'markdown',
97+
});
98+
99+
// Check against current top-level license for changes
100+
const shouldWarnChanges =
101+
process.argv.includes('--test') && formattedLicense !== (await readFile('LICENSE.md', 'utf-8'));
102+
103+
// Top level license
104+
await writeFile('LICENSE.md', formattedLicense, 'utf-8');
105+
106+
// License file for each package as well, so that we publish it to npm
107+
108+
const packages = ['lwc', ...atLwcPackages];
109+
110+
await Promise.all(
111+
packages.map(async (pkg) => {
112+
await writeFile(path.join('packages/', pkg, 'LICENSE.md'), formattedLicense, 'utf-8');
113+
})
114+
);
115+
116+
if (shouldWarnChanges) {
117+
const relativeFilename = path.relative(process.cwd(), import.meta.filename);
118+
throw new Error(
119+
'Either the LWC core license or the license of a bundled dependency has been updated.\n' +
120+
`Please run \`node ${relativeFilename}\` and commit the result.`
121+
);
122+
}

0 commit comments

Comments
 (0)