Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: rewrite preparation script in TS to avoid cross-platform issues #1938

Merged
merged 4 commits into from
Jan 9, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"scripts": {
"clean:build": "nx run-many -t clean:build --all --output-style stream",
"check": "nx run-many -t lint,typecheck --all --output-style stream",
"build": "sh ./scripts/prepare.sh && nx run-many -t build --all --output-style stream",
"build": "tsx scripts/prepare.ts && nx run-many -t build --all --output-style stream",
"build:nocache": "npm run build",
"lint": "nx run-many -t lint --all --output-style stream",
"lint:fix": "nx run-many -t lint:fix --all --output-style stream",
Expand Down
69 changes: 69 additions & 0 deletions scripts/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
export type Version = {
javaVersion?: string;
jsVersion?: string;
npmName?: string;
};

export type Versions = {
bundles: Record<string, Version>;
core: Record<string, Version>;
kits: Record<string, Version>;
platform: string;
react: Record<string, Version>;
vaadin: Record<string, Version>;
};

export interface Writer {
writeReleaseNotes(versions: Versions, templateFileName: string, resultFileName: string): void;
writeSeparateJson(
versions: Record<string, Version> | string,
templateFileName: string,
resultFileName: string,
key: keyof Versions,
): void;
}

export interface Transformer {
transformVersions(versions: Versions, version: string, isPrerelease: boolean): Versions;
}

// TODO: compute this number when we maintain multiple hilla branches
export const branch = '24.3.0';

export const repoUrl = new URL('https://raw.githubusercontent.com/vaadin/');
export const root = new URL('../', import.meta.url);
export const rel = {
src: 'scripts/generator/src',
results: 'scripts/generator/results',
};

export const local = {
src: new URL(`${rel.src}/`, root),
versionedPackageJson: new URL('packages/ts/generator-typescript-core/package.json', root),
results: new URL(`${rel.results}/`, root),
};

export const remote = {
// https://raw.githubusercontent.com/vaadin/platform/24.3.0/scripts/generator/src/writer.js
src: new URL(`platform/${branch}/${rel.src}/`, repoUrl),
versions: new URL(`platform/${branch}/versions.json`, repoUrl),
lumo: new URL(
`flow-components/${branch}/vaadin-lumo-theme-flow-parent/vaadin-lumo-theme-flow/src/main/java/com/vaadin/flow/theme/lumo/Lumo.java`,
repoUrl,
),
material: new URL(
`flow-components/${branch}/vaadin-material-theme-flow-parent/vaadin-material-theme-flow/src/main/java/com/vaadin/flow/theme/material/Material.java`,
repoUrl,
),
};

export const destination = {
lit: {
versions: new URL('packages/java/hilla/hilla-versions.json', root),
themeDir: new URL('packages/java/hilla/src/main/java/dev/hilla/theme/', root),
},
react: {
versions: new URL('packages/java/hilla-react/hilla-react-versions.json', root),
themeDir: new URL('packages/java/hilla-react/src/main/java/dev/hilla/theme/', root),
},
};
55 changes: 0 additions & 55 deletions scripts/generator/generate.js

This file was deleted.

120 changes: 120 additions & 0 deletions scripts/generator/generate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/* eslint-disable no-console */
import { createRequire } from 'node:module';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { Script } from 'node:vm';
import { remote, type Transformer, type Versions, type Writer } from '../config.js';

const require = createRequire(import.meta.url);

async function loadScripts(): Promise<readonly [Writer, Transformer]> {
const [creator, replacer, transformer, writer] = await Promise.all(
['creator', 'replacer', 'transformer', 'writer'].map(async (name) => {
const url = new URL(`./${name}.js`, remote.src);
const res = await fetch(url);
const code = await res.text();
return new Script(code, { filename: url.toString() });
}),
);

function req(name: string): unknown {
let script: Script;

if (name.includes('creator')) {
script = creator;
} else if (name.includes('replacer')) {
script = replacer;
} else if (name.includes('writer')) {
script = writer;
} else if (name.includes('transformer')) {
script = transformer;
} else {
return require(name);
}

const exports = {};

const ctx = {
exports,
module: { exports },
require: req,
};
script.runInNewContext(ctx);

return ctx.module.exports;
}

return [req('writer') as Writer, req('transformer') as Transformer];
}

const [writer, transformer] = await loadScripts();

const [
hillaJsonTemplateFileName,
hillaReactJsonTemplateFileName,
releaseNotesTemplateFileName,
releaseNotesMaintenanceTemplateFileName,
releaseNotesPrereleaseTemplateFileName,
] = [
'template-hilla-versions.json',
'template-hilla-react-versions.json',
'template-release-note.md',
'template-release-note-maintenance.md',
'template-release-note-prerelease.md',
].map((name) => fileURLToPath(new URL(`templates/${name}`, import.meta.url)));

const [
hillaJsonResultFileName,
hillaReactJsonResultFileName,
releaseNotesResultFileName,
releaseNotesMaintenanceResultFileName,
releaseNotesPrereleaseResultFileName,
] = [
'hilla-versions.json',
'hilla-react-versions.json',
'release-note.md',
'release-note-maintenance.md',
'release-note-prerelease.md',
].map((name) => fileURLToPath(new URL(`results/${name}`, import.meta.url)));

export default function generate(version: string, versions: Versions): void {
console.log('Generating release files');

const transformed = transformer.transformVersions(versions, version, false);
transformed.platform = version;

writer.writeSeparateJson(transformed.bundles, hillaJsonTemplateFileName, hillaJsonResultFileName, 'bundles');
writer.writeSeparateJson(transformed.core, hillaJsonTemplateFileName, hillaJsonResultFileName, 'core');
writer.writeSeparateJson(transformed.vaadin, hillaJsonTemplateFileName, hillaJsonResultFileName, 'vaadin');
writer.writeSeparateJson(
transformed.bundles,
hillaReactJsonTemplateFileName,
hillaReactJsonResultFileName,
'bundles',
);

console.log(`Generated ${pathToFileURL(hillaJsonResultFileName).toString()}.`);

writer.writeSeparateJson(transformed.react, hillaReactJsonTemplateFileName, hillaReactJsonResultFileName, 'react');

console.log(`Generated ${pathToFileURL(hillaReactJsonResultFileName).toString()}.`);

writer.writeReleaseNotes(transformed, releaseNotesTemplateFileName, releaseNotesResultFileName);

console.log(`Generated ${pathToFileURL(releaseNotesResultFileName).toString()}.`);

writer.writeReleaseNotes(transformed, releaseNotesMaintenanceTemplateFileName, releaseNotesMaintenanceResultFileName);

console.log(`Generated ${pathToFileURL(releaseNotesMaintenanceResultFileName).toString()}.`);

writer.writeReleaseNotes(transformed, releaseNotesPrereleaseTemplateFileName, releaseNotesPrereleaseResultFileName);

console.log(`Generated ${pathToFileURL(releaseNotesPrereleaseResultFileName).toString()}.`);

transformed.core.hilla = { javaVersion: version };

// write hilla version to hilla-react-versions.json as platform
writer.writeSeparateJson(version, hillaJsonTemplateFileName, hillaJsonResultFileName, 'platform');
writer.writeSeparateJson(version, hillaReactJsonTemplateFileName, hillaReactJsonResultFileName, 'platform');

console.log('"hilla-versions.json" and "hilla-react-versions.json" files are updated with the platform version');
}
3 changes: 0 additions & 3 deletions scripts/generator/package.json

This file was deleted.

49 changes: 0 additions & 49 deletions scripts/prepare.sh

This file was deleted.

60 changes: 60 additions & 0 deletions scripts/prepare.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/* eslint-disable no-console */
import { copyFile, mkdir, readFile, writeFile } from 'node:fs/promises';
import type { PackageJson } from 'type-fest';
import { destination, local, remote, type Versions } from './config.js';
import generate from './generator/generate.js';

const [{ version }, versions] = await Promise.all([
readFile(local.versionedPackageJson, 'utf-8').then(JSON.parse) as Promise<PackageJson>,
// download needed files from vaadin/platform
fetch(remote.versions)
.then(async (res) => await res.text())
.then((str) => JSON.parse(str, (_, val) => (val === '{{version}}' ? undefined : val))) as Promise<Versions>,
mkdir(local.src, { recursive: true }),
mkdir(local.results, { recursive: true }),
mkdir(destination.lit.themeDir, { recursive: true }),
mkdir(destination.react.themeDir, { recursive: true }),
]);

if (!version) {
throw new Error('No version found in package.json of Hilla "generator-typescript-core"');
}

// run the generator
generate(version, versions);

console.log('Moving the generated files to the final place.');

await Promise.all([
copyFile(new URL('hilla-versions.json', local.results), destination.lit.versions).then(() =>
console.log(`Moved ${destination.lit.versions.toString()}`),
),
copyFile(new URL('hilla-react-versions.json', local.results), destination.react.versions).then(() =>
console.log(`Moved ${destination.react.versions.toString()}`),
),
]);

const themeAnnotationsPattern = /.*(JsModule|NpmPackage).*\n/gmu;
const themeFiles = new Map([
[remote.lumo, [new URL('Lumo.java', destination.lit.themeDir), new URL('Lumo.java', destination.react.themeDir)]],
[
remote.material,
[new URL('Material.java', destination.lit.themeDir), new URL('Material.java', destination.react.themeDir)],
],
]);

console.log('Copying the theme files from flow-components to the final place.');

await Promise.all(
Array.from(themeFiles.entries(), async ([url, dest]) => {
const response = await fetch(url);
let code = await response.text();
code = code.replaceAll(themeAnnotationsPattern, '');
await Promise.all(
dest.map(async (file) => {
await writeFile(file, code);
console.log(`Copied ${file.toString()}`);
}),
);
}),
);
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@
"useDefineForClassFields": true,
"useUnknownInCatchVariables": true
},
"include": ["scripts/*.ts", "scripts/*.d.ts", "./*.ts", "./*.d.ts", "./*.js", "./*.cjs"]
"include": ["scripts/**/*.ts", "scripts/*.d.ts", "./*.ts", "./*.d.ts", "./*.js", "./*.cjs"]
}
Loading