diff --git a/package.json b/package.json index 1c746c28..5b0aa922 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "build": "tsc --build tsconfig.build.json", "build:watch": "tsc --build --watch tsconfig.build.json", "build:logos": "npm run build:logos -w resources/logos", - "create-package": "./scripts/create-package.js", + "create-package": "./scripts/create-package/execute.ts", "lint": "npm run lint:biome && npm run lint:tsc", "lint:biome": "biome check", "lint:tsc": "tsc", diff --git a/resources/logos/package.json b/resources/logos/package.json index 73039fa5..881adcdd 100644 --- a/resources/logos/package.json +++ b/resources/logos/package.json @@ -16,7 +16,7 @@ "node": "^22.12 || ^24" }, "scripts": { - "build:logos": "./scripts/build.js" + "build:logos": "./scripts/build.ts" }, "dependencies": { "sharp": "^0.34.4", diff --git a/resources/logos/scripts/build.js b/resources/logos/scripts/build.ts similarity index 100% rename from resources/logos/scripts/build.js rename to resources/logos/scripts/build.ts diff --git a/scripts/create-package.js b/scripts/create-package.js deleted file mode 100755 index 26b3e69a..00000000 --- a/scripts/create-package.js +++ /dev/null @@ -1,117 +0,0 @@ -#!/usr/bin/env node -// biome-ignore-all lint/suspicious/noConsole: only used in local dev - -import fs from 'node:fs/promises'; -import path from 'node:path'; -import releasePleaseManifest from '../.release-please-manifest.json' with { type: 'json' }; -import rootManifest from '../package.json' with { type: 'json' }; -import releasePleaseConfig from '../release-please-config.json' with { type: 'json' }; - -// Get the package name -const name = process.argv[2]; -if (!name) { - throw new Error('No package name provided'); -} - -// Create the package directory -const packagePath = path.resolve(import.meta.dirname, '..', 'packages', name); -console.log(`📂 creating package directory "packages/${name}"`); -await fs.mkdir(packagePath); - -// Create a package manifest -const manifest = { - name: `@dotcom-reliability-kit/${name}`, - version: '0.0.0', - type: 'module', - description: 'TODO', - repository: { - type: 'git', - url: 'https://github.com/Financial-Times/dotcom-reliability-kit.git', - directory: `packages/${name}` - }, - homepage: `https://github.com/Financial-Times/dotcom-reliability-kit/tree/main/packages/${name}#readme`, - bugs: `https://github.com/Financial-Times/dotcom-reliability-kit/issues?q=label:"package: ${name}"`, - license: rootManifest.license, - scripts: { - test: 'npm run test:unit && npm run test:end-to-end', - 'test:unit': - "node --test --experimental-test-module-mocks --experimental-test-coverage --test-coverage-exclude='**/*.spec.js' --test-coverage-branches=100 --test-coverage-functions=100 --test-coverage-lines=100 'test/unit/**/*.spec.js'", - 'test:end-to-end': "node --test 'test/end-to-end/**/*.spec.js'" - }, - engines: rootManifest.engines, - exports: { - '.': { - types: './types/index.d.ts', - default: './lib/index.js' - } - } -}; -console.log('📦 initialising "package.json"'); -await fs.writeFile(path.join(packagePath, 'package.json'), JSON.stringify(manifest, null, 2)); - -console.log('📖 writing "README.md"'); -await fs.writeFile( - path.join(packagePath, 'README.md'), - ` -# @dotcom-reliability-kit/${name} - -This module is part of [FT.com Reliability Kit](https://github.com/Financial-Times/dotcom-reliability-kit#readme). -` -); - -// Create an npm ignore file -console.log('📄 adding ".npmignore"'); -await fs.writeFile( - path.join(packagePath, '.npmignore'), - ['CHANGELOG.md', 'docs', 'test'].join('\n') -); - -// Bootstrap base JavaScript files -console.log('🏗 adding "lib/index.js"'); -const libPath = path.join(packagePath, 'lib'); -await fs.mkdir(libPath); -await fs.writeFile( - path.join(libPath, 'index.js'), - `export {}; -` -); -console.log('🏗 adding "types/index.d.ts"'); -const typesPath = path.join(packagePath, 'types'); -await fs.mkdir(typesPath); -await fs.writeFile( - path.join(typesPath, 'index.d.ts'), - `declare module '@dotcom-reliability-kit/${name}' {} -` -); - -// Bootstrap test JavaScript files -console.log('🏗 adding "test/unit/lib/index.spec.js"'); -const testPath = path.join(packagePath, 'test', 'unit', 'lib'); -await fs.mkdir(testPath, { recursive: true }); -await fs.writeFile( - path.join(testPath, 'index.spec.js'), - `import assert from 'node:assert/strict'; -import { describe, it } from 'node:test'; - -describe('@dotcom-reliability-kit/${name}', () => { - it('has some tests', () => { - assert.strictEqual(true, false); - }); -}); -` -); - -// Add package to Release Please config -console.log('🚢 adding package to Release Please config'); -releasePleaseConfig.packages[`packages/${name}`] = {}; -await fs.writeFile( - path.resolve(import.meta.dirname, '..', 'release-please-config.json'), - JSON.stringify(releasePleaseConfig, null, '\t') -); - -console.log('🚢 adding package to Release Please manifest'); -releasePleaseManifest[`packages/${name}`] = '0.0.0'; -await fs.writeFile( - path.resolve(import.meta.dirname, '..', '.release-please-manifest.json'), - JSON.stringify(releasePleaseManifest, null, '\t') -); diff --git a/scripts/create-package/execute.ts b/scripts/create-package/execute.ts new file mode 100755 index 00000000..19e57e08 --- /dev/null +++ b/scripts/create-package/execute.ts @@ -0,0 +1,75 @@ +#!/usr/bin/env node +// biome-ignore-all lint/suspicious/noConsole: only used in local dev + +import { glob, mkdir, readFile, writeFile } from 'node:fs/promises'; +import { dirname, join, resolve } from 'node:path'; +import releasePleaseManifest from '../../.release-please-manifest.json' with { type: 'json' }; +import releasePleaseConfig from '../../release-please-config.json' with { type: 'json' }; +import typeScriptBuildConfig from '../../tsconfig.build.json' with { type: 'json' }; + +// Get the package name +const name = process.argv[2]; +if (!name) { + console.error('❌ No package name provided'); + process.exit(1); +} +if (!/^[a-z][a-z0-9-]+$/.test(name)) { + console.error('❌ Package name must be lowercase and alphanumeric with dashes'); + process.exit(1); +} + +// Get other package details +const year = new Date().getFullYear(); + +// Create the package directory +const packagePath = resolve(import.meta.dirname, '..', '..', 'packages', name); +console.log(`📂 creating package directory "packages/${name}"`); +await mkdir(packagePath, { recursive: true }); + +// Copy across all template files +const templatePath = join(import.meta.dirname, 'template'); +for await (const template of glob('**/*.*', { cwd: templatePath })) { + const inputPath = join(templatePath, template); + const outputPath = join(packagePath, template); + await mkdir(dirname(outputPath), { recursive: true }); + + const input = await readFile(inputPath, 'utf-8'); + const output = input.replaceAll('{{name}}', name).replaceAll('{{year}}', `${year}`); + + console.log(`📝 writing "${template}"`); + await writeFile(outputPath, output); +} + +// Add package to Release Please config (re-ordering the packages) +console.log('🚢 adding package to Release Please config'); +releasePleaseConfig.packages[`packages/${name}`] = {}; +releasePleaseConfig.packages = Object.fromEntries( + Object.entries(releasePleaseConfig.packages).toSorted(([a], [b]) => (a > b ? 1 : -1)) +) as typeof releasePleaseConfig.packages; +await writeFile( + resolve(import.meta.dirname, '..', '..', 'release-please-config.json'), + `${JSON.stringify(releasePleaseConfig, null, '\t')}\n` +); + +console.log('🚢 adding package to Release Please manifest'); +releasePleaseManifest[`packages/${name}`] = '0.0.0'; +const sortedReleasePleaseManifest = Object.fromEntries( + Object.entries(releasePleaseManifest).toSorted(([a], [b]) => (a > b ? 1 : -1)) +) as typeof releasePleaseManifest; +await writeFile( + resolve(import.meta.dirname, '..', '..', '.release-please-manifest.json'), + JSON.stringify(sortedReleasePleaseManifest, null, 2) // Must be two spaces because Release Please sets it to this +); + +// Add package to TypeScript build config +console.log('🏗️ adding package to TypeScript build config'); +if (!typeScriptBuildConfig.references.find(({ path }) => path === `packages/${name}`)) { + typeScriptBuildConfig.references.push({ path: `packages/${name}` }); + typeScriptBuildConfig.references = typeScriptBuildConfig.references.toSorted( + ({ path: a }, { path: b }) => (a > b ? 1 : -1) + ); + await writeFile( + resolve(import.meta.dirname, '..', '..', 'tsconfig.build.json'), + `${JSON.stringify(typeScriptBuildConfig, null, '\t')}\n` + ); +} diff --git a/scripts/create-package/template/README.md b/scripts/create-package/template/README.md new file mode 100644 index 00000000..4e5ff3e2 --- /dev/null +++ b/scripts/create-package/template/README.md @@ -0,0 +1,30 @@ + +# @dotcom-reliability-kit/{{name}} + +This module is part of [FT.com Reliability Kit](https://github.com/Financial-Times/dotcom-reliability-kit#readme). + +* [Usage](#usage) +* [Contributing](#contributing) +* [License](#license) + + +## Usage + +Install `@dotcom-reliability-kit/{{name}}` as a dependency: + +```bash +npm install --save @dotcom-reliability-kit/{{name}} +``` + +TODO write a usage guide. + + +## Contributing + +See the [central contributing guide for Reliability Kit](https://github.com/Financial-Times/dotcom-reliability-kit/blob/main/docs/contributing.md). + + +## License + +Licensed under the [MIT](https://github.com/Financial-Times/dotcom-reliability-kit/blob/main/LICENSE) license.
+Copyright © {{year}}, The Financial Times Ltd. diff --git a/scripts/create-package/template/index.ts b/scripts/create-package/template/index.ts new file mode 100644 index 00000000..cb0ff5c3 --- /dev/null +++ b/scripts/create-package/template/index.ts @@ -0,0 +1 @@ +export {}; diff --git a/scripts/create-package/template/package.json b/scripts/create-package/template/package.json new file mode 100644 index 00000000..af49f477 --- /dev/null +++ b/scripts/create-package/template/package.json @@ -0,0 +1,32 @@ +{ + "name": "@dotcom-reliability-kit/{{name}}", + "version": "0.0.0", + "type": "module", + "description": "TODO write a description", + "repository": { + "type": "git", + "url": "https://github.com/Financial-Times/dotcom-reliability-kit.git", + "directory": "packages/{{name}}" + }, + "homepage": "https://github.com/Financial-Times/dotcom-reliability-kit/tree/main/packages/{{name}}#readme", + "bugs": "https://github.com/Financial-Times/dotcom-reliability-kit/issues?q=label:\"package: {{name}}\"", + "license": "MIT", + "scripts": { + "test": "npm run test:unit && npm run test:end-to-end", + "test:unit": "node --test --experimental-test-module-mocks --experimental-test-coverage --test-coverage-exclude='**/*.spec.ts' --test-coverage-branches=100 --test-coverage-functions=100 --test-coverage-lines=100 'test/unit/**/*.spec.ts'", + "test:end-to-end": "node --test 'test/end-to-end/**/*.spec.ts'" + }, + "engines": { + "node": "^22.12 || ^24" + }, + "exports": { + ".": { + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "files": [ + "dist", + "!*.tsbuildinfo" + ] +} diff --git a/scripts/create-package/template/test/unit/index.spec.ts b/scripts/create-package/template/test/unit/index.spec.ts new file mode 100644 index 00000000..3e80f180 --- /dev/null +++ b/scripts/create-package/template/test/unit/index.spec.ts @@ -0,0 +1,8 @@ +import assert from 'node:assert/strict'; +import { describe, it } from 'node:test'; + +describe('@dotcom-reliability-kit/{{name}}', () => { + it('has some tests', () => { + assert.strictEqual(true, false); + }); +}); diff --git a/scripts/create-package/template/tsconfig.json b/scripts/create-package/template/tsconfig.json new file mode 100644 index 00000000..8a27d081 --- /dev/null +++ b/scripts/create-package/template/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "noEmit": false, + "outDir": "dist" + }, + "exclude": ["dist", "test"] +}