All checks were successful
CI / build-lint-test (pull_request) Successful in 34s
pnpm 12.4.2 n'ecrivait pas d'entree importers pour packages/common/service-registry dans le lockfile, meme apres suppression complete du lockfile et du cache d'etat pnpm puis reinstallation (pnpm ls -r reconnaissait pourtant bien le package). CI echouait avec --frozen-lockfile (erreur has no importers[...] entry), alors que le pnpm install local sans --frozen-lockfile ne se plaignait de rien. Cause probable : ce package ne declarait aucune dependency ni devDependency propre. Remede : declarer explicitement quelques devDependencies (typescript, vitest, @types/node via catalog:), meme deja hoistees depuis la racine. create-package.mjs generera desormais ces devDependencies par defaut pour tout nouveau package, et le troubleshooting est documente dans spec/ci-cd.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
164 lines
5.1 KiB
JavaScript
164 lines
5.1 KiB
JavaScript
#!/usr/bin/env node
|
|
import { execFileSync } from 'node:child_process';
|
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
import { dirname, join } from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const ROOT = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
const CATEGORIES = ['client', 'common', 'server'];
|
|
|
|
/**
|
|
* Prints usage information and exits with an error status.
|
|
* @param {string} message the reason the script is aborting
|
|
*/
|
|
function fail(message) {
|
|
console.error(`Erreur : ${message}`);
|
|
console.error('Usage : pnpm create-package <client|common|server> <nom-du-package>');
|
|
process.exitCode = 1;
|
|
process.exit(1);
|
|
}
|
|
|
|
const [, , category, name] = process.argv;
|
|
|
|
if (!category || !CATEGORIES.includes(category)) {
|
|
fail(`catégorie invalide (attendu : ${CATEGORIES.join(', ')})`);
|
|
}
|
|
if (!name || !/^[a-z][a-z0-9-]*[a-z0-9]$/.test(name)) {
|
|
fail('nom de package invalide (attendu : kebab-case, ex. "my-widget")');
|
|
}
|
|
|
|
const packageDir = join(ROOT, 'packages', category, name);
|
|
if (existsSync(packageDir)) {
|
|
fail(`packages/${category}/${name} existe déjà`);
|
|
}
|
|
|
|
const npmName = `@lightweight-sdk/${name}`;
|
|
const isClient = category === 'client';
|
|
|
|
mkdirSync(join(packageDir, 'src'), { recursive: true });
|
|
|
|
const packageJson = {
|
|
name: npmName,
|
|
version: '0.0.1',
|
|
description: `TODO: décrire le package ${name}.`,
|
|
main: 'dist/index.js',
|
|
types: 'dist/index.d.ts',
|
|
files: ['dist'],
|
|
engines: { node: '>=22' },
|
|
publishConfig: {
|
|
access: 'restricted',
|
|
registry: 'https://git.dev.kyuno.fr/api/packages/kyuno/npm/',
|
|
},
|
|
typedocOptions: {
|
|
entryPoints: ['src/index.ts'],
|
|
readme: 'README.md',
|
|
},
|
|
// Déclarées explicitement (même si déjà présentes à la racine, où elles
|
|
// seraient hoistées) : un package workspace sans aucune dépendance propre
|
|
// peut se retrouver sans entrée dans pnpm-lock.yaml, ce qui casse
|
|
// `pnpm install --frozen-lockfile` en CI (voir spec/ci-cd.md).
|
|
devDependencies: {
|
|
'@types/node': 'catalog:',
|
|
typescript: 'catalog:',
|
|
vitest: 'catalog:',
|
|
},
|
|
};
|
|
writeFileSync(join(packageDir, 'package.json'), `${JSON.stringify(packageJson, null, '\t')}\n`);
|
|
|
|
const tsconfig = {
|
|
extends: '../../../tsconfig.base.json',
|
|
compilerOptions: isClient
|
|
? { lib: ['ES2022', 'DOM', 'DOM.Iterable'], outDir: './dist', rootDir: './src' }
|
|
: { outDir: './dist', rootDir: './src' },
|
|
include: ['src/**/*'],
|
|
};
|
|
writeFileSync(join(packageDir, 'tsconfig.json'), `${JSON.stringify(tsconfig, null, '\t')}\n`);
|
|
|
|
const indexTs = `/**
|
|
* Placeholder function for the ${npmName} package — replace with real exports.
|
|
* @param subject the name to greet
|
|
* @returns a greeting for the given subject
|
|
*/
|
|
export function greet(subject: string): string {
|
|
return \`Hello, \${subject}!\`;
|
|
}
|
|
`;
|
|
writeFileSync(join(packageDir, 'src', 'index.ts'), indexTs);
|
|
|
|
const indexTest = `import { describe, expect, it } from 'vitest';
|
|
import { greet } from './index';
|
|
|
|
describe('greet', () => {
|
|
it('greets the given subject', () => {
|
|
expect(greet('world')).toBe('Hello, world!');
|
|
});
|
|
});
|
|
`;
|
|
writeFileSync(join(packageDir, 'src', 'index.test.ts'), indexTest);
|
|
|
|
const commonNote =
|
|
category === 'common'
|
|
? "\n> `common/` doit rester isomorphe : pas d'API exclusivement DOM ou exclusivement Node.\n"
|
|
: '';
|
|
const readme = `# ${npmName}
|
|
${commonNote}
|
|
TODO: décrire le package.
|
|
|
|
## Installation
|
|
|
|
\`\`\`bash
|
|
pnpm add ${npmName}
|
|
\`\`\`
|
|
|
|
## Usage
|
|
|
|
TODO
|
|
`;
|
|
writeFileSync(join(packageDir, 'README.md'), readme);
|
|
|
|
// Édite le tsconfig.json racine (project references)
|
|
const rootTsconfigPath = join(ROOT, 'tsconfig.json');
|
|
const rootTsconfig = JSON.parse(readFileSync(rootTsconfigPath, 'utf8'));
|
|
rootTsconfig.references ??= [];
|
|
rootTsconfig.references.push({ path: `./packages/${category}/${name}` });
|
|
writeFileSync(rootTsconfigPath, `${JSON.stringify(rootTsconfig, null, '\t')}\n`);
|
|
|
|
// Ajoute une ligne au registre des packages
|
|
const packagesSpecPath = join(ROOT, 'spec', 'packages.md');
|
|
const packagesSpec = readFileSync(packagesSpecPath, 'utf8');
|
|
const newRow = `| \`${npmName}\` | ${category} | 0.0.1 | brouillon |\n`;
|
|
writeFileSync(packagesSpecPath, packagesSpec.trimEnd() + '\n' + newRow);
|
|
|
|
console.log(`Package ${npmName} créé dans packages/${category}/${name}/.`);
|
|
|
|
try {
|
|
// shell: true est nécessaire sous Windows, où `pnpm` est un shim .cmd/.ps1
|
|
// que execFileSync ne résout pas via PATHEXT sans passer par un shell.
|
|
execFileSync('pnpm', ['install'], { cwd: ROOT, stdio: 'inherit', shell: true });
|
|
execFileSync(
|
|
'pnpm',
|
|
[
|
|
'exec',
|
|
'biome',
|
|
'check',
|
|
'--write',
|
|
`packages/${category}/${name}`,
|
|
'tsconfig.json',
|
|
'spec/packages.md',
|
|
],
|
|
{ cwd: ROOT, stdio: 'inherit', shell: true },
|
|
);
|
|
} catch (error) {
|
|
console.error('Étape automatique échouée, à relancer toi-même :');
|
|
console.error(' pnpm install');
|
|
console.error(
|
|
` pnpm exec biome check --write packages/${category}/${name} tsconfig.json spec/packages.md`,
|
|
);
|
|
console.error(String(error));
|
|
}
|
|
|
|
console.log('\nProchaines étapes :');
|
|
console.log(` cd packages/${category}/${name}`);
|
|
console.log(' - compléter la description dans package.json et README.md');
|
|
console.log(` - pnpm --filter ${npmName} test`);
|
|
console.log(' - pnpm changeset (une fois prêt à publier)');
|