build: establish strict TypeScript workspace
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
node_modules
|
||||
coverage
|
||||
pnpm-lock.yaml
|
||||
packages/**/test/fixtures
|
||||
packages/test-fixtures/src/**/*.json
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 100
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "@multi-simadmin/api",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": "./src/index.ts",
|
||||
"scripts": {
|
||||
"typecheck": "tsc -p tsconfig.json"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export const apiWorkspaceReady = true;
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "@multi-simadmin/web",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": "./src/index.ts",
|
||||
"scripts": {
|
||||
"typecheck": "tsc -p tsconfig.json"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export const webWorkspaceReady = true;
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import eslint from '@eslint/js';
|
||||
import { defineConfig } from 'eslint/config';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
export default defineConfig(
|
||||
{
|
||||
ignores: ['node_modules/**', 'coverage/**', 'packages/**/test/fixtures/**'],
|
||||
},
|
||||
{
|
||||
files: ['**/*.js'],
|
||||
...eslint.configs.recommended,
|
||||
languageOptions: {
|
||||
...eslint.configs.recommended.languageOptions,
|
||||
globals: {
|
||||
console: 'readonly',
|
||||
process: 'readonly',
|
||||
URL: 'readonly',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
extends: [...tseslint.configs.recommended],
|
||||
languageOptions: {
|
||||
parser: tseslint.parser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'no-undef': 'off',
|
||||
},
|
||||
},
|
||||
);
|
||||
Generated
+2948
-3
File diff suppressed because it is too large
Load Diff
+19
-3
@@ -2,14 +2,30 @@
|
||||
"name": "multi-simadmin",
|
||||
"version": "0.1.0",
|
||||
"description": "Config-driven dashboard to manage multiple SimAdmin instances from one page",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@11.13.0",
|
||||
"scripts": {
|
||||
"start": "node server/index.js",
|
||||
"dev": "node --watch server/index.js",
|
||||
"test": "node --test"
|
||||
"test": "node --test test/*.test.js packages/operation-registry/test/*.test.ts packages/test-fixtures/test/*.test.ts",
|
||||
"test:legacy": "node --test test/*.test.js",
|
||||
"test:unit": "vitest run",
|
||||
"test:contract": "node --test packages/operation-registry/test/*.test.ts",
|
||||
"lint": "eslint apps packages/contracts test/phase-one-workspace.test.js eslint.config.js vitest.config.ts",
|
||||
"format:check": "prettier --check apps packages/contracts test/phase-one-workspace.test.js eslint.config.js vitest.config.ts tsconfig.base.json pnpm-workspace.yaml package.json",
|
||||
"typecheck": "corepack pnpm --recursive --if-present run typecheck"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/static": "^9.1.3",
|
||||
"fastify": "^5.6.2"
|
||||
"@fastify/static": "9.1.3",
|
||||
"fastify": "5.10.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "9.39.2",
|
||||
"eslint": "9.39.2",
|
||||
"prettier": "3.8.1",
|
||||
"typescript": "5.9.3",
|
||||
"typescript-eslint": "8.53.0",
|
||||
"vitest": "4.0.18"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "@multi-simadmin/contracts",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": "./src/index.ts",
|
||||
"scripts": {
|
||||
"typecheck": "tsc -p tsconfig.json"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { contractsWorkspaceReady } from './index.js';
|
||||
|
||||
describe('contracts workspace baseline', () => {
|
||||
it('exposes a compilable package entry', () => {
|
||||
expect(contractsWorkspaceReady).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
export const contractsWorkspaceReady = true;
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": "src"
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "@multi-simadmin/operation-registry",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "@multi-simadmin/test-fixtures",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"type": "module"
|
||||
}
|
||||
Generated
+2298
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,8 @@
|
||||
packages:
|
||||
- 'apps/*'
|
||||
- 'packages/*'
|
||||
|
||||
# Vitest uses esbuild for TypeScript transformation. pnpm 11 blocks every
|
||||
# dependency build by default, so keep the allow-list explicit and minimal.
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
@@ -0,0 +1,93 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import test from 'node:test';
|
||||
|
||||
const root = new URL('../', import.meta.url);
|
||||
const readJson = async (path) => JSON.parse(await readFile(new URL(path, root), 'utf8'));
|
||||
|
||||
const workspacePackages = ['apps/api', 'apps/web', 'packages/contracts'];
|
||||
|
||||
test('Phase 1 workspace pins pnpm and preserves the legacy service gates', async () => {
|
||||
const packageJson = await readJson('package.json');
|
||||
|
||||
assert.equal(packageJson.packageManager, 'pnpm@11.13.0');
|
||||
for (const script of ['lint', 'format:check', 'typecheck', 'test:unit', 'test:contract']) {
|
||||
assert.equal(typeof packageJson.scripts[script], 'string', `missing ${script} script`);
|
||||
}
|
||||
assert.equal(packageJson.scripts.start, 'node server/index.js');
|
||||
assert.equal(packageJson.scripts.dev, 'node --watch server/index.js');
|
||||
assert.match(
|
||||
packageJson.scripts.test,
|
||||
/node --test/,
|
||||
'root test must retain the legacy node:test gate',
|
||||
);
|
||||
assert.match(packageJson.scripts.test, /test\/\*\.test\.js/);
|
||||
assert.match(packageJson.scripts.test, /packages\/operation-registry\/test\/\*\.test\.ts/);
|
||||
assert.match(packageJson.scripts.test, /packages\/test-fixtures\/test\/\*\.test\.ts/);
|
||||
assert.doesNotMatch(
|
||||
packageJson.scripts.test,
|
||||
/^node --test$/,
|
||||
'root test must not auto-discover Vitest-only files',
|
||||
);
|
||||
assert.match(packageJson.scripts.lint, /apps/);
|
||||
assert.match(packageJson.scripts.lint, /packages\/contracts/);
|
||||
assert.match(packageJson.scripts.lint, /eslint\.config\.js/);
|
||||
assert.doesNotMatch(
|
||||
packageJson.scripts.lint,
|
||||
/eslint\s+\./,
|
||||
'Phase 1 lint must not claim unmigrated legacy code',
|
||||
);
|
||||
assert.doesNotMatch(packageJson.scripts.lint, /echo|true/, 'lint must execute a real checker');
|
||||
assert.match(packageJson.scripts['format:check'], /prettier --check/);
|
||||
assert.match(
|
||||
packageJson.scripts.typecheck,
|
||||
/^corepack pnpm /,
|
||||
'typecheck must work when pnpm is provided only by Corepack',
|
||||
);
|
||||
});
|
||||
|
||||
test('Phase 1 workspace and strict shared TypeScript policy are explicit', async () => {
|
||||
const workspace = await readFile(new URL('pnpm-workspace.yaml', root), 'utf8');
|
||||
assert.match(workspace, /^packages:\s*$/m);
|
||||
assert.match(workspace, /- ['"]apps\/\*['"]/);
|
||||
assert.match(workspace, /- ['"]packages\/\*['"]/);
|
||||
|
||||
const tsconfig = await readJson('tsconfig.base.json');
|
||||
const required = {
|
||||
strict: true,
|
||||
noUncheckedIndexedAccess: true,
|
||||
exactOptionalPropertyTypes: true,
|
||||
useUnknownInCatchVariables: true,
|
||||
noImplicitOverride: true,
|
||||
};
|
||||
for (const [option, value] of Object.entries(required)) {
|
||||
assert.equal(tsconfig.compilerOptions[option], value, `${option} must be enabled`);
|
||||
}
|
||||
});
|
||||
|
||||
test('initial application and contract packages compile from isolated source entries', async () => {
|
||||
for (const directory of workspacePackages) {
|
||||
const manifest = await readJson(`${directory}/package.json`);
|
||||
const tsconfig = await readJson(`${directory}/tsconfig.json`);
|
||||
await readFile(new URL(`${directory}/src/index.ts`, root), 'utf8');
|
||||
|
||||
const serialized = JSON.stringify({ manifest, tsconfig });
|
||||
assert.doesNotMatch(
|
||||
serialized,
|
||||
/(?:^|[/.])server(?:[/.]|$)/,
|
||||
`${directory} must not depend on legacy server`,
|
||||
);
|
||||
assert.doesNotMatch(
|
||||
serialized,
|
||||
/(?:^|[/.])public(?:[/.]|$)/,
|
||||
`${directory} must not depend on legacy public assets`,
|
||||
);
|
||||
assert.equal(tsconfig.extends, '../../tsconfig.base.json');
|
||||
}
|
||||
});
|
||||
|
||||
test('runtime dependencies stay pinned to the versions already present before workspace migration', async () => {
|
||||
const packageJson = await readJson('package.json');
|
||||
assert.equal(packageJson.dependencies.fastify, '5.10.0');
|
||||
assert.equal(packageJson.dependencies['@fastify/static'], '9.1.3');
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"lib": ["ES2022"],
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"exactOptionalPropertyTypes": true,
|
||||
"useUnknownInCatchVariables": true,
|
||||
"noImplicitOverride": true,
|
||||
"noEmit": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"isolatedModules": true,
|
||||
"skipLibCheck": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
include: ['apps/**/*.test.ts', 'packages/contracts/**/*.test.ts'],
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user