From dc191b7beca83badd4b9ee2737e23e1804583223 Mon Sep 17 00:00:00 2001 From: rdlabo Date: Mon, 28 Sep 2026 20:44:14 +0900 Subject: [PATCH 1/3] feat: share packed ESM checks across theme packages --- .github/workflows/ci.yml | 3 +- README.md | 31 ++++++++++++++ package-lock.json | 3 +- package.json | 14 +++--- scripts/check-esm.mjs | 62 +++++++++++++++++++++++++++ scripts/check-esm.test.mjs | 88 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 194 insertions(+), 7 deletions(-) create mode 100755 scripts/check-esm.mjs create mode 100644 scripts/check-esm.test.mjs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d36acee..f74b1eb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,6 +24,7 @@ jobs: - run: npm run fmt:check - run: npm run lint - run: npm test + - run: npm run test:cli - run: npm run build - name: Verify Node ESM output - run: node --input-type=module -e "await import('./dist/index.js')" + run: npm run test:esm diff --git a/README.md b/README.md index f9b8b73..f41151d 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,37 @@ positioning, searchable tab-bar utilities, and structured-list Sass mixins. ## Development +### Check a theme's ESM package + +This package provides `rdlabo-check-esm` for the iOS 26, iOS 27, and MD3 themes. +Add it to the consuming theme's npm scripts: + +```json +{ + "scripts": { + "test:esm": "rdlabo-check-esm" + } +} +``` + +Run `npm run build && npm run test:esm` from the theme's root directory. An +explicit package directory can also be passed as the first argument. The command +requires Node.js 24 or later, npm, tar, and TypeScript installed in the target +package's development dependencies. + +The check creates and extracts an npm tarball without running lifecycle scripts, +requires `"type": "module"`, verifies explicit `.js` extensions and existing file +targets for relative imports in `dist/` (including literal dynamic imports), and +loads each public JavaScript entry point declared by an `exports` entry with an +`import` target. It supports the themes' explicit entry points; wildcard exports +and nested conditions are not supported. Installed dependencies are reused, while +the theme itself is loaded from the tarball. UI operations are not executed. + +TypeScript is loaded from the package being checked, so the CLI does not add it +as a runtime dependency of the shared utilities. + +### Work on the shared utilities + ```shell npm install npm run build diff --git a/package-lock.json b/package-lock.json index fb61e00..8221769 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,8 @@ "version": "0.1.1", "license": "MIT", "bin": { - "rdlabo-copy-structured-list": "scripts/copy-structured-list.mjs" + "rdlabo-copy-structured-list": "scripts/copy-structured-list.mjs", + "rdlabo-check-esm": "scripts/check-esm.mjs" }, "devDependencies": { "@eslint/js": "^9.39.5", diff --git a/package.json b/package.json index f9624f2..7ea00aa 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "module": "./dist/index.js", "types": "./dist/index.d.ts", "bin": { - "rdlabo-copy-structured-list": "./scripts/copy-structured-list.mjs" + "rdlabo-copy-structured-list": "./scripts/copy-structured-list.mjs", + "rdlabo-check-esm": "./scripts/check-esm.mjs" }, "exports": { ".": { @@ -25,17 +26,20 @@ "scripts/copy-structured-list.mjs", "package.json", "README.md", - "LICENSE" + "LICENSE", + "scripts/check-esm.mjs" ], "scripts": { "build": "tsc", "test": "vitest run", - "fmt:check": "prettier --check \"./**/*.{js,json,md,scss,ts,yml}\"", + "fmt:check": "prettier --check \"./**/*.{js,json,md,mjs,scss,ts,yml}\"", "lint": "eslint .", - "fmt": "prettier --write \"./**/*.{js,json,md,scss,ts,yml}\"", + "fmt": "prettier --write \"./**/*.{js,json,md,mjs,scss,ts,yml}\"", "release": "np --branch main --no-tests --no-publish", "prepublishOnly": "npm run build", - "prepare": "npm run build" + "prepare": "npm run build", + "test:esm": "node scripts/check-esm.mjs", + "test:cli": "node --test scripts/check-esm.test.mjs" }, "repository": { "type": "git", diff --git a/scripts/check-esm.mjs b/scripts/check-esm.mjs new file mode 100755 index 0000000..3cf8e47 --- /dev/null +++ b/scripts/check-esm.mjs @@ -0,0 +1,62 @@ +#!/usr/bin/env node + +import assert from 'node:assert/strict'; +import { execFileSync } from 'node:child_process'; +import { mkdtempSync, readFileSync, readdirSync, rmSync, statSync, symlinkSync } from 'node:fs'; +import { tmpdir } from 'node:os'; +import { dirname, join, resolve } from 'node:path'; +import { createRequire } from 'node:module'; +import process from 'node:process'; + +const root = resolve(process.argv[2] ?? process.cwd()); +const ts = createRequire(join(root, 'package.json'))('typescript'); +const temporary = mkdtempSync(join(tmpdir(), 'ionic-theme-esm-')); + +try { + const packed = JSON.parse( + execFileSync('npm', ['pack', '--ignore-scripts', '--json', '--pack-destination', temporary], { cwd: root, encoding: 'utf8' }), + ); + execFileSync('tar', ['-xzf', join(temporary, packed[0].filename), '-C', temporary]); + const directory = join(temporary, 'package'); + const manifest = JSON.parse(readFileSync(join(directory, 'package.json'), 'utf8')); + assert.equal(manifest.type, 'module'); + + // Check lazy imports as well as the graph reached by importing the public APIs. + for (const relative of readdirSync(join(directory, 'dist'), { recursive: true }).filter((file) => file.endsWith('.js'))) { + const file = join(directory, 'dist', relative); + const source = ts.createSourceFile(file, readFileSync(file, 'utf8'), ts.ScriptTarget.Latest, true, ts.ScriptKind.JS); + const visit = (node) => { + const specifier = + ts.isImportDeclaration(node) || ts.isExportDeclaration(node) + ? node.moduleSpecifier + : ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword + ? node.arguments[0] + : undefined; + if (specifier && ts.isStringLiteral(specifier) && specifier.text.startsWith('.')) { + assert.ok(specifier.text.endsWith('.js'), `${relative}: missing .js extension in ${specifier.text}`); + assert.ok(statSync(resolve(dirname(file), specifier.text)).isFile(), `${relative}: invalid target ${specifier.text}`); + } + ts.forEachChild(node, visit); + }; + visit(source); + } + + // Reuse installed dependencies, but resolve the theme itself from its tarball. + symlinkSync(join(root, 'node_modules'), join(temporary, 'node_modules'), 'dir'); + const entries = Object.entries(manifest.exports) + .filter(([, target]) => typeof target === 'object' && typeof target.import === 'string') + .map(([subpath]) => manifest.name + subpath.slice(1)); + assert.ok(entries.length > 0, 'No public ESM entry points found'); + execFileSync( + process.execPath, + [ + '--input-type=module', + '-e', + `for (const name of ${JSON.stringify(entries)}) { await import(name); process.stdout.write('Imported ' + name + '\\n'); }`, + ], + { cwd: directory, stdio: 'inherit' }, + ); + process.stdout.write('Packed ESM imports and relative specifiers verified.\n'); +} finally { + rmSync(temporary, { recursive: true, force: true }); +} diff --git a/scripts/check-esm.test.mjs b/scripts/check-esm.test.mjs new file mode 100644 index 0000000..8817683 --- /dev/null +++ b/scripts/check-esm.test.mjs @@ -0,0 +1,88 @@ +import assert from 'node:assert/strict'; +import { execFileSync, spawnSync } from 'node:child_process'; +import { mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'; +import { createRequire } from 'node:module'; +import { tmpdir } from 'node:os'; +import { dirname, join } from 'node:path'; +import process from 'node:process'; +import { after, test } from 'node:test'; +import { fileURLToPath, URL } from 'node:url'; + +const root = fileURLToPath(new URL('../', import.meta.url)); +const temporary = mkdtempSync(join(tmpdir(), 'theme-esm-cli-test-')); +after(() => rmSync(temporary, { recursive: true, force: true })); + +// Run the published CLI without the utils repository's development dependencies. +const [archive] = JSON.parse( + execFileSync('npm', ['pack', '--ignore-scripts', '--json', '--pack-destination', temporary], { cwd: root, encoding: 'utf8' }), +); +execFileSync('tar', ['-xzf', join(temporary, archive.filename), '-C', temporary]); +const manifest = JSON.parse(readFileSync(join(temporary, 'package/package.json'), 'utf8')); +const cli = join(temporary, 'package', manifest.bin['rdlabo-check-esm']); + +const fixture = (name, { type = 'module', lazy = './detail.js', native = 'export const native = true;', files = ['dist'] } = {}) => { + const directory = join(temporary, name); + mkdirSync(join(directory, 'dist'), { recursive: true }); + mkdirSync(join(directory, 'node_modules'), { recursive: true }); + const require = createRequire(import.meta.url); + symlinkSync(dirname(require.resolve('typescript/package.json')), join(directory, 'node_modules/typescript'), 'dir'); + writeFileSync( + join(directory, 'package.json'), + JSON.stringify({ + name: `theme-${name}`, + version: '1.0.0', + type, + files, + exports: { '.': { import: './dist/index.js' }, './native': { import: './dist/native.js' } }, + scripts: { prepack: 'exit 1', prepare: 'exit 1' }, + }), + ); + writeFileSync(join(directory, 'dist/index.js'), "export { value } from './detail.js';"); + writeFileSync(join(directory, 'dist/detail.js'), 'export const value = 1;'); + writeFileSync(join(directory, 'dist/native.js'), native); + writeFileSync(join(directory, 'dist/lazy.js'), `export const load = () => import(${JSON.stringify(lazy)});`); + return directory; +}; + +const run = (directory, explicit = false) => { + const result = spawnSync(process.execPath, [cli, ...(explicit ? [directory] : [])], { + cwd: explicit ? temporary : directory, + encoding: 'utf8', + timeout: 30000, + }); + assert.ifError(result.error); + return { ...result, output: result.stdout + result.stderr }; +}; + +test('checks the consuming package from cwd or an explicit directory, without running lifecycle scripts', () => { + const directory = fixture('valid'); + for (const explicit of [false, true]) { + const result = run(directory, explicit); + assert.equal(result.status, 0, result.output); + assert.match(result.stdout, /Imported theme-valid\nImported theme-valid\/native\n/); + } +}); + +test('rejects extensionless lazy imports even outside the public import graph', () => { + const result = run(fixture('extensionless', { lazy: './detail' })); + assert.notEqual(result.status, 0); + assert.match(result.output, /missing \.js extension in \.\/detail/); +}); + +test('rejects files omitted from the npm tarball', () => { + const result = run(fixture('missing-target', { files: ['dist/index.js', 'dist/native.js'] })); + assert.notEqual(result.status, 0); + assert.match(result.output, /ENOENT.*detail\.js/); +}); + +test('requires an explicit ESM package declaration', () => { + const result = run(fixture('commonjs', { type: 'commonjs' })); + assert.notEqual(result.status, 0); + assert.match(result.output, /commonjs/); +}); + +test('imports subpath entry points and catches import-time DOM access', () => { + const result = run(fixture('dom-access', { native: 'export const body = document.body;' })); + assert.notEqual(result.status, 0); + assert.match(result.output, /document is not defined/); +}); From 86f4919003f5a4d8f899c155d15a4428182baa69 Mon Sep 17 00:00:00 2001 From: rdlabo Date: Mon, 28 Sep 2026 21:04:50 +0900 Subject: [PATCH 2/3] feat: generate theme ESM with shared tsdown configuration --- README.md | 32 +- package-lock.json | 737 ++++++++++++++++++++++++++++++++- package.json | 8 +- scripts/build-theme.mjs | 17 + scripts/check-esm.mjs | 16 +- scripts/check-esm.test.mjs | 57 ++- scripts/theme-build.config.mjs | 16 + 7 files changed, 872 insertions(+), 11 deletions(-) create mode 100755 scripts/build-theme.mjs create mode 100644 scripts/theme-build.config.mjs diff --git a/README.md b/README.md index f41151d..0e167b0 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,35 @@ positioning, searchable tab-bar utilities, and structured-list Sass mixins. ## Development +### Build a theme's ESM package + +`rdlabo-build-theme` uses a shared tsdown configuration so theme sources can keep +extensionless imports, matching Ionic's source style. Add `tsdown` (`^0.23.0`) and +TypeScript to the theme's development dependencies and use: + +```json +{ + "scripts": { + "build:ts": "tsc --noEmit && rdlabo-build-theme", + "test:esm": "rdlabo-check-esm" + } +} +``` + +Set `"type": "module"` in the theme's package.json and use `"module": "ESNext"` +with `"moduleResolution": "bundler"` in its tsconfig.json. Keep CommonJS tool +configurations in `.cjs` files. + +The command builds `src/**/*.ts` (excluding tests and declaration inputs) into +`dist/`, preserving the module structure and generating ESM JavaScript, type +declarations, and declaration maps. tsdown resolves static, directory, and dynamic +imports during the build. Dependencies stay external. Existing `dist/css` files +are preserved; use a fresh output directory for release builds to avoid stale +files. The command accepts an optional package directory as its first argument. + +The target package supplies tsdown, so this adds no bundler runtime dependency to +the shared utilities. Run the same build during development, CI, and publication. + ### Check a theme's ESM package This package provides `rdlabo-check-esm` for the iOS 26, iOS 27, and MD3 themes. @@ -42,7 +71,8 @@ package's development dependencies. The check creates and extracts an npm tarball without running lifecycle scripts, requires `"type": "module"`, verifies explicit `.js` extensions and existing file -targets for relative imports in `dist/` (including literal dynamic imports), and +targets for relative JavaScript and declaration imports in `dist/` (including +literal dynamic imports), and loads each public JavaScript entry point declared by an `exports` entry with an `import` target. It supports the themes' explicit entry points; wildcard exports and nested conditions are not supported. Installed dependencies are reused, while diff --git a/package-lock.json b/package-lock.json index 8221769..14e2289 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,8 +9,9 @@ "version": "0.1.1", "license": "MIT", "bin": { - "rdlabo-copy-structured-list": "scripts/copy-structured-list.mjs", - "rdlabo-check-esm": "scripts/check-esm.mjs" + "rdlabo-build-theme": "scripts/build-theme.mjs", + "rdlabo-check-esm": "scripts/check-esm.mjs", + "rdlabo-copy-structured-list": "scripts/copy-structured-list.mjs" }, "devDependencies": { "@eslint/js": "^9.39.5", @@ -19,6 +20,7 @@ "jsdom": "^26.1.0", "np": "^10.3.0", "prettier": "^3.6.2", + "tsdown": "^0.23.0", "typescript": "^5.9.3", "typescript-eslint": "^8.70.0", "vitest": "^4.1.11" @@ -866,6 +868,19 @@ "node": ">=12" } }, + "node_modules/@quansync/fs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@quansync/fs/-/fs-1.1.0.tgz", + "integrity": "sha512-qAPG/t3HqML1TlN7sY/pTbEjzFVAKsMjNNMGheyDosro+kT4iw2KCUoHcVdmliwWjorm4elZbgNQyU2eD97eDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quansync": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, "node_modules/@rolldown/binding-android-arm-eabi": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.8.tgz", @@ -1739,6 +1754,385 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@yuku-codegen/binding-android-arm64": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-android-arm64/-/binding-android-arm64-0.10.2.tgz", + "integrity": "sha512-Oc2KInVkPfUjEB4PeV6X0NvIoyYTzDe/WmqFrTwBqKs6YKnz4A7XwWQvJTb7M0rLz9a6WgpUUiiS5QKaCi4O/g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@yuku-codegen/binding-darwin-arm64": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-darwin-arm64/-/binding-darwin-arm64-0.10.2.tgz", + "integrity": "sha512-3H7eNPIHJndUJXZ4QUGBPq1zC6RGcUZfm7bymJes+/N7wTVWUn1bxZ9JcozYHpkWQNm9f23G8YWbaHD9aYH72A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@yuku-codegen/binding-darwin-x64": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-darwin-x64/-/binding-darwin-x64-0.10.2.tgz", + "integrity": "sha512-AJOUpR2s3LF9AWiiub/Uyc/UoXNTWcq8hK3cVI6fUSXtwh34dG21J6hRuNlO6JVJFTo4o3ScVvZOrh/YFfUEAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@yuku-codegen/binding-freebsd-x64": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-freebsd-x64/-/binding-freebsd-x64-0.10.2.tgz", + "integrity": "sha512-ms7DcZu87u5CiK/wMwvpKAvAmtaqKjCQIXNweMLypG6qbuiaOMQf1jpbB5+j46xBcgCovo8TQMcv0bCQLKIL9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@yuku-codegen/binding-linux-arm-gnu": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-linux-arm-gnu/-/binding-linux-arm-gnu-0.10.2.tgz", + "integrity": "sha512-xJDpYAsKV5+eaiBhTYl05fvT6sst4PsCeuIFMRu9b0/WCSrNQPfCDtAQ5/MS6xNpsdujdZEPR+gmfWk3ZG5i3A==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-codegen/binding-linux-arm-musl": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-linux-arm-musl/-/binding-linux-arm-musl-0.10.2.tgz", + "integrity": "sha512-qfTkgd7AEx61l4K9VtXWCGTxc/fmXJ4ZheDz3i+/AAJUtg4sa0bPrc0VBxPggB/rayR8Z3+JfrBItuyanBJJ9Q==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-codegen/binding-linux-arm64-gnu": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.10.2.tgz", + "integrity": "sha512-4e6Mifm/4UdjtU3D8mATIrvgP+xEiH8xtQOjd0zpd72XKWT7ug3sdyhq2utkkZFP6BvYp7SgZrI+aR4VeIOHdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-codegen/binding-linux-arm64-musl": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.10.2.tgz", + "integrity": "sha512-RdsJrUfDYFVV3JOmWFUWwSu31fa1APn12xDeIKxgl/YWxrabwtxsDBNjF2561c7tmcbiewdCUvngqRs8AyGVLA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-codegen/binding-linux-x64-gnu": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.10.2.tgz", + "integrity": "sha512-mVzWimEPPreaPyXIUvxsHoJzvO7ckZ5j0LgQFHz04zQIRwt9A9T2hA7K5/jYjJKkMxWG/U2VjhGNwVhtyU+uqg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-codegen/binding-linux-x64-musl": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-linux-x64-musl/-/binding-linux-x64-musl-0.10.2.tgz", + "integrity": "sha512-Y77fyISurmr2mvO1yeq3u+x/WJbACgPR4w+lzEVx30ViCxZzIGrZPRN1yEdZ9xUNPNsIO5thBHIdRNG+UGUG+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-codegen/binding-win32-arm64": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-win32-arm64/-/binding-win32-arm64-0.10.2.tgz", + "integrity": "sha512-1+tGLyG0u5YYy0lev4ck7/0sd8xJ9SZde0dLut7qLDPZV5WVTV8x6OxNXUne/PGuHZyO1vK+txPQzzHOyXHGSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@yuku-codegen/binding-win32-x64": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-codegen/binding-win32-x64/-/binding-win32-x64-0.10.2.tgz", + "integrity": "sha512-8a2SExRohRbwC0MY4VpOF0/RSckrJ2ASZqAor5/RYSJJaeadR93n10gZzcKT6pY9ukiSOIZCRKVRD2tH73T0qA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@yuku-parser/binding-android-arm64": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-android-arm64/-/binding-android-arm64-0.10.2.tgz", + "integrity": "sha512-2VPBU9fRGRAQ2xPAvghnec3oou5Nrxm2Bezhf+13UMspAxQSkF/32r+ySKXSwIPA5/RivumDJDxAxc301Sgicw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@yuku-parser/binding-darwin-arm64": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.10.2.tgz", + "integrity": "sha512-LD+PMZE51tCYTOss5HBkm3/AE39MvcMDBfWJx7A4yDcjfNbAQDnHZKtzSOuqrswx+TQHY0ws5xn6+fWOwtmfBA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@yuku-parser/binding-darwin-x64": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-darwin-x64/-/binding-darwin-x64-0.10.2.tgz", + "integrity": "sha512-mmZ8cND+AoIIMRERyMinlg5ApHxP23B9jH2B5wT7T+dliPa9rubLxneB/SUjFwyUjGaFnB5G7t4YvpfbO5zbkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@yuku-parser/binding-freebsd-x64": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.10.2.tgz", + "integrity": "sha512-gVIjaaIddbRfAhHlC8N809wQWml7mxfSVnzaLtzGXObTeFTPAg/YVnQXt1UOhPM1ah5eG/8Y0RUlNpB4GVe7eQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@yuku-parser/binding-linux-arm-gnu": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-linux-arm-gnu/-/binding-linux-arm-gnu-0.10.2.tgz", + "integrity": "sha512-q/XPPQQAPdlw05aPj30ygBhekmQryGOwxVraBgApjKK8yY1kNQgqq6XCYLF1WHSee+lhxclrTO7W0/bt7dR1GA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-parser/binding-linux-arm-musl": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-linux-arm-musl/-/binding-linux-arm-musl-0.10.2.tgz", + "integrity": "sha512-A+Cb0I1hFF4wilTQpWs79k1aNnj4B2tkrHx3zsuUNF9BtVY2zPZ4yeQEM/zjXrS/qJlNMZVK9NrWJjwdpnTrfg==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-parser/binding-linux-arm64-gnu": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.10.2.tgz", + "integrity": "sha512-aGNSzqIqqphFwAdIFwVvKIyXD1Iy3CxrEJVIZAT87Ecyi4vCmmDD2v2l9h+Gd3/wSy3JZlOI792LJbKAjyy9rQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-parser/binding-linux-arm64-musl": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.10.2.tgz", + "integrity": "sha512-Ddl1sF0rtuCXyHGSOV6dcmdx+ETv1iD+IVFqQuOjzkJZWclxJxdBWX6ZJMRtTiGqGUTbqLEKiTXxpR/Bvqk+2A==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-parser/binding-linux-x64-gnu": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.10.2.tgz", + "integrity": "sha512-/nlcpR6IF5U+0m5L9wvAIXeQa2DT+BXuvqKDsTcOTlcc0B4dHNyqE5hTXsS4hAyimpy2ZK8A1zMNF5hTrjg2cg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-parser/binding-linux-x64-musl": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.10.2.tgz", + "integrity": "sha512-GX80dxTQD/M/OryyuxJcFzFENzry3cDFPvCFTyWogNWQH3fSHfMrNfpwpl1YzMos1eV9NygK5GSDG7VArQlb4w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@yuku-parser/binding-win32-arm64": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-win32-arm64/-/binding-win32-arm64-0.10.2.tgz", + "integrity": "sha512-agePQBV4VHewiGU0ACSjscZ/hJd283f9JfAF19Gf1rI5+wy5tTgx4GS36i0b3xim15AQ4RCpDRosEvhLZ4zAOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@yuku-parser/binding-win32-x64": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-parser/binding-win32-x64/-/binding-win32-x64-0.10.2.tgz", + "integrity": "sha512-s8//CMpgL5+y1lvDCdyh1rwGI5+ytDJywFJRe1vnhI7n0j+caxshNucurikYLLVeQ2SYFex6aPrzgQxkabBQRA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@yuku-toolchain/types": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@yuku-toolchain/types/-/types-0.10.2.tgz", + "integrity": "sha512-sSeo4SSSToiS+sSD+bwn/s94EEcaLJ7tG5LCp8gFYC1G5VzxzX7fqB6m9RU1yMD8KTpu6zdU/I1NlQf8hVBJ9Q==", + "dev": true, + "license": "MIT" + }, "node_modules/acorn": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", @@ -2102,6 +2496,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cac": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cac/-/cac-7.0.0.tgz", + "integrity": "sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2488,6 +2892,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/defu": { + "version": "6.1.7", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.7.tgz", + "integrity": "sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==", + "dev": true, + "license": "MIT" + }, "node_modules/del": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/del/-/del-8.0.1.tgz", @@ -2549,6 +2960,27 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/dts-resolver": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/dts-resolver/-/dts-resolver-3.0.0.tgz", + "integrity": "sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "oxc-resolver": ">=11.0.0" + }, + "peerDependenciesMeta": { + "oxc-resolver": { + "optional": true + } + } + }, "node_modules/elegant-spinner": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/elegant-spinner/-/elegant-spinner-1.0.1.tgz", @@ -2566,6 +2998,16 @@ "dev": true, "license": "MIT" }, + "node_modules/empathic": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/empathic/-/empathic-2.1.0.tgz", + "integrity": "sha512-AnfC1ATldl49/cvZdLPDjBfrRNwbDO05aibiOtzQu3qtlbJtomNLhF30HEtn/7iBz50dlMECqATo3fG0LrdEgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/entities": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", @@ -3106,6 +3548,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-tsconfig": { + "version": "5.0.0-beta.6", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-5.0.0-beta.6.tgz", + "integrity": "sha512-X6fBC0pmImC70gvX2zm56go9hx0MyoGVdG0tUCkg/D+Xnh5TJsOZ7iDbOdI3PvmtrDxnu1YdDufpK2QJX1Meqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-pkg-maps": "^1.0.0" + }, + "engines": { + "node": ">=20.20.0" + }, + "funding": { + "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" + } + }, "node_modules/github-url-from-git": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/github-url-from-git/-/github-url-from-git-1.5.0.tgz", @@ -3229,6 +3687,13 @@ "node": ">=8" } }, + "node_modules/hookable": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-6.1.2.tgz", + "integrity": "sha512-+abwxtiEA52GCVIsQqut3S/uKTbUwYIp4Pe/vv+6py5XiXBCqMZHg6pA6Y5qhgLSEys0/cYuPbO0z1QFj5ZCmg==", + "dev": true, + "license": "MIT" + }, "node_modules/hosted-git-info": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", @@ -3392,6 +3857,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-without-cache": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/import-without-cache/-/import-without-cache-0.4.1.tgz", + "integrity": "sha512-vXoV9PjKHEednCUu01e98TkImxy67e3BJXbTIOmIq4Hyzw3IAwYRcuPkfeTzJzwyyts4kjuA73x+pWJLc4f86A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^22.18.0 || >=24.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -5983,6 +6461,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/quansync": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-1.0.0.tgz", + "integrity": "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -6194,6 +6689,16 @@ "node": ">=4" } }, + "node_modules/resolve-pkg-maps": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" + } + }, "node_modules/restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", @@ -6283,6 +6788,66 @@ "@rolldown/binding-win32-x64-msvc": "1.2.8" } }, + "node_modules/rolldown-plugin-dts": { + "version": "0.28.6", + "resolved": "https://registry.npmjs.org/rolldown-plugin-dts/-/rolldown-plugin-dts-0.28.6.tgz", + "integrity": "sha512-qKrFtBfRfR2hP233m7Ic9zf3wv6MSYdZghWKMdEO6dRYZGlNfINJAa1P5ZVvyHh3mrcd7IJQvIY8L9vnm+v5rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dts-resolver": "^3.0.0", + "get-tsconfig": "5.0.0-beta.6", + "obug": "^3.0.0", + "yuku-ast": "^0.10.1", + "yuku-codegen": "^0.10.1", + "yuku-parser": "^0.10.1" + }, + "engines": { + "node": "^22.18.0 || ^24.11.0 || >=26.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@typescript/native-preview": "*", + "@volar/typescript": "~2.4.0", + "@vue/language-core": "~3.2.0 || ~3.3.0", + "rolldown": "^1.2.0", + "typescript": "^5.0.0 || ^6.0.0 || ~7.0.0", + "vue-tsc": "~3.2.0 || ~3.3.0" + }, + "peerDependenciesMeta": { + "@typescript/native-preview": { + "optional": true + }, + "@volar/typescript": { + "optional": true + }, + "@vue/language-core": { + "optional": true + }, + "typescript": { + "optional": true + }, + "vue-tsc": { + "optional": true + } + } + }, + "node_modules/rolldown-plugin-dts/node_modules/obug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/obug/-/obug-3.0.0.tgz", + "integrity": "sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/rrweb-cssom": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", @@ -6803,6 +7368,16 @@ "node": ">=18" } }, + "node_modules/tree-kill": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dev": true, + "license": "MIT", + "bin": { + "tree-kill": "cli.js" + } + }, "node_modules/ts-api-utils": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", @@ -6816,6 +7391,78 @@ "typescript": ">=4.8.4" } }, + "node_modules/tsdown": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/tsdown/-/tsdown-0.23.0.tgz", + "integrity": "sha512-BaT+ep1xnj5hdyICLd5r5SYYE+TXnI1ATePLykv9vcKpHpFTWUWRH+x1AF/E2qcu3YB6+vI8IdyxIIIffEqw5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^7.0.0", + "defu": "^6.1.7", + "empathic": "^2.0.1", + "hookable": "^6.1.1", + "import-without-cache": "^0.4.0", + "obug": "^2.1.4", + "picomatch": "^4.0.7", + "rolldown": "~1.2.7", + "rolldown-plugin-dts": "^0.28.5", + "tinyexec": "^1.3.1", + "tinyglobby": "^0.2.17", + "tree-kill": "^1.2.2", + "unconfig-core": "^7.5.0", + "verkit": "^0.4.0" + }, + "bin": { + "tsdown": "dist/run.mjs" + }, + "engines": { + "node": "^22.18.0 || ^24.11.0 || >=26.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + }, + "peerDependencies": { + "@arethetypeswrong/core": "^0.18.1", + "@tsdown/css": "0.23.0", + "@tsdown/exe": "0.23.0", + "@vitejs/devtools": "*", + "publint": "^0.3.8", + "tsx": "*", + "typescript": "^5.0.0 || ^6.0.0 || ^7.0.0", + "unplugin-unused": ">=0.5.0", + "unrun": "*" + }, + "peerDependenciesMeta": { + "@arethetypeswrong/core": { + "optional": true + }, + "@tsdown/css": { + "optional": true + }, + "@tsdown/exe": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "publint": { + "optional": true + }, + "tsx": { + "optional": true + }, + "typescript": { + "optional": true + }, + "unplugin-unused": { + "optional": true + }, + "unrun": { + "optional": true + } + } + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", @@ -6887,6 +7534,20 @@ "typescript": ">=4.8.4 <6.1.0" } }, + "node_modules/unconfig-core": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/unconfig-core/-/unconfig-core-7.5.0.tgz", + "integrity": "sha512-Su3FauozOGP44ZmKdHy2oE6LPjk51M/TRRjHv2HNCWiDvfvCoxC2lno6jevMA91MYAdCdwP05QnWdWpSbncX/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@quansync/fs": "^1.0.0", + "quansync": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/unicorn-magic": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", @@ -6969,6 +7630,19 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/verkit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/verkit/-/verkit-0.4.1.tgz", + "integrity": "sha512-mMVzj0TXExtVdlDEq+Mzp0eyOuyznNpFobNM3uAqe3HVm/Ps2c+u17BligMkZjZCA6EiXpoon8iRTer3iu40SQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/sxzz" + } + }, "node_modules/vite": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.0.tgz", @@ -7487,6 +8161,65 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } + }, + "node_modules/yuku-ast": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/yuku-ast/-/yuku-ast-0.10.2.tgz", + "integrity": "sha512-UnG9mA6giglCvSErft2/40TVFX750Sj1xgwddPLpG7J7rlr/P1wPADg9G2RK+d/1tNLtRVoLdMMOMVBB9561TQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@yuku-toolchain/types": "^0.10.2" + } + }, + "node_modules/yuku-codegen": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/yuku-codegen/-/yuku-codegen-0.10.2.tgz", + "integrity": "sha512-hentl2dtrF6cPjiAirtkfxfFZBA6Hdm61UqRJ7cA0qrlDNMk9PZGOjw5w1mx3E0hu/6pHcJF4dJW+D0SXHPZOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@yuku-toolchain/types": "^0.10.2" + }, + "optionalDependencies": { + "@yuku-codegen/binding-android-arm64": "0.10.2", + "@yuku-codegen/binding-darwin-arm64": "0.10.2", + "@yuku-codegen/binding-darwin-x64": "0.10.2", + "@yuku-codegen/binding-freebsd-x64": "0.10.2", + "@yuku-codegen/binding-linux-arm-gnu": "0.10.2", + "@yuku-codegen/binding-linux-arm-musl": "0.10.2", + "@yuku-codegen/binding-linux-arm64-gnu": "0.10.2", + "@yuku-codegen/binding-linux-arm64-musl": "0.10.2", + "@yuku-codegen/binding-linux-x64-gnu": "0.10.2", + "@yuku-codegen/binding-linux-x64-musl": "0.10.2", + "@yuku-codegen/binding-win32-arm64": "0.10.2", + "@yuku-codegen/binding-win32-x64": "0.10.2" + } + }, + "node_modules/yuku-parser": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/yuku-parser/-/yuku-parser-0.10.2.tgz", + "integrity": "sha512-CgaU0/PPjCAIEZ3WQroosOxTY3eeKldAN3h+vk8pMNz4+jl1CZzBR4pW+K/rRPF112VooCL5FdjJoiMNjDrL2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@yuku-toolchain/types": "^0.10.2", + "yuku-ast": "^0.10.2" + }, + "optionalDependencies": { + "@yuku-parser/binding-android-arm64": "0.10.2", + "@yuku-parser/binding-darwin-arm64": "0.10.2", + "@yuku-parser/binding-darwin-x64": "0.10.2", + "@yuku-parser/binding-freebsd-x64": "0.10.2", + "@yuku-parser/binding-linux-arm-gnu": "0.10.2", + "@yuku-parser/binding-linux-arm-musl": "0.10.2", + "@yuku-parser/binding-linux-arm64-gnu": "0.10.2", + "@yuku-parser/binding-linux-arm64-musl": "0.10.2", + "@yuku-parser/binding-linux-x64-gnu": "0.10.2", + "@yuku-parser/binding-linux-x64-musl": "0.10.2", + "@yuku-parser/binding-win32-arm64": "0.10.2", + "@yuku-parser/binding-win32-x64": "0.10.2" + } } } } diff --git a/package.json b/package.json index 7ea00aa..7a6a6ec 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "types": "./dist/index.d.ts", "bin": { "rdlabo-copy-structured-list": "./scripts/copy-structured-list.mjs", - "rdlabo-check-esm": "./scripts/check-esm.mjs" + "rdlabo-check-esm": "./scripts/check-esm.mjs", + "rdlabo-build-theme": "./scripts/build-theme.mjs" }, "exports": { ".": { @@ -27,7 +28,9 @@ "package.json", "README.md", "LICENSE", - "scripts/check-esm.mjs" + "scripts/check-esm.mjs", + "scripts/build-theme.mjs", + "scripts/theme-build.config.mjs" ], "scripts": { "build": "tsc", @@ -67,6 +70,7 @@ "jsdom": "^26.1.0", "np": "^10.3.0", "prettier": "^3.6.2", + "tsdown": "^0.23.0", "typescript": "^5.9.3", "typescript-eslint": "^8.70.0", "vitest": "^4.1.11" diff --git a/scripts/build-theme.mjs b/scripts/build-theme.mjs new file mode 100755 index 0000000..7baa4f1 --- /dev/null +++ b/scripts/build-theme.mjs @@ -0,0 +1,17 @@ +#!/usr/bin/env node + +import { spawnSync } from 'node:child_process'; +import { createRequire } from 'node:module'; +import { join, resolve } from 'node:path'; +import process from 'node:process'; +import { fileURLToPath, URL } from 'node:url'; + +const root = resolve(process.argv[2] ?? process.cwd()); +const require = createRequire(join(root, 'package.json')); +const result = spawnSync( + process.execPath, + [require.resolve('tsdown/run'), '--config', fileURLToPath(new URL('./theme-build.config.mjs', import.meta.url))], + { cwd: root, stdio: 'inherit' }, +); +if (result.error) throw result.error; +process.exitCode = result.status ?? 1; diff --git a/scripts/check-esm.mjs b/scripts/check-esm.mjs index 3cf8e47..cbb69c5 100755 --- a/scripts/check-esm.mjs +++ b/scripts/check-esm.mjs @@ -21,20 +21,26 @@ try { const manifest = JSON.parse(readFileSync(join(directory, 'package.json'), 'utf8')); assert.equal(manifest.type, 'module'); - // Check lazy imports as well as the graph reached by importing the public APIs. - for (const relative of readdirSync(join(directory, 'dist'), { recursive: true }).filter((file) => file.endsWith('.js'))) { + // Check declarations and lazy imports as well as the public JavaScript graph. + for (const relative of readdirSync(join(directory, 'dist'), { recursive: true }).filter( + (file) => file.endsWith('.js') || file.endsWith('.d.ts'), + )) { const file = join(directory, 'dist', relative); - const source = ts.createSourceFile(file, readFileSync(file, 'utf8'), ts.ScriptTarget.Latest, true, ts.ScriptKind.JS); + const declaration = relative.endsWith('.d.ts'); + const source = ts.createSourceFile(file, readFileSync(file, 'utf8'), ts.ScriptTarget.Latest, true); const visit = (node) => { const specifier = ts.isImportDeclaration(node) || ts.isExportDeclaration(node) ? node.moduleSpecifier : ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword ? node.arguments[0] - : undefined; + : ts.isImportTypeNode(node) && ts.isLiteralTypeNode(node.argument) + ? node.argument.literal + : undefined; if (specifier && ts.isStringLiteral(specifier) && specifier.text.startsWith('.')) { assert.ok(specifier.text.endsWith('.js'), `${relative}: missing .js extension in ${specifier.text}`); - assert.ok(statSync(resolve(dirname(file), specifier.text)).isFile(), `${relative}: invalid target ${specifier.text}`); + const target = declaration ? specifier.text.replace(/\.js$/, '.d.ts') : specifier.text; + assert.ok(statSync(resolve(dirname(file), target)).isFile(), `${relative}: invalid target ${specifier.text}`); } ts.forEachChild(node, visit); }; diff --git a/scripts/check-esm.test.mjs b/scripts/check-esm.test.mjs index 8817683..20953b4 100644 --- a/scripts/check-esm.test.mjs +++ b/scripts/check-esm.test.mjs @@ -1,6 +1,6 @@ import assert from 'node:assert/strict'; import { execFileSync, spawnSync } from 'node:child_process'; -import { mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'; +import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'; import { createRequire } from 'node:module'; import { tmpdir } from 'node:os'; import { dirname, join } from 'node:path'; @@ -19,6 +19,7 @@ const [archive] = JSON.parse( execFileSync('tar', ['-xzf', join(temporary, archive.filename), '-C', temporary]); const manifest = JSON.parse(readFileSync(join(temporary, 'package/package.json'), 'utf8')); const cli = join(temporary, 'package', manifest.bin['rdlabo-check-esm']); +const buildCli = join(temporary, 'package', manifest.bin['rdlabo-build-theme']); const fixture = (name, { type = 'module', lazy = './detail.js', native = 'export const native = true;', files = ['dist'] } = {}) => { const directory = join(temporary, name); @@ -86,3 +87,57 @@ test('imports subpath entry points and catches import-time DOM access', () => { assert.notEqual(result.status, 0); assert.match(result.output, /document is not defined/); }); + +test('rejects extensionless references in declarations', () => { + const directory = fixture('declaration'); + writeFileSync(join(directory, 'dist/index.d.ts'), "export type Value = import('./detail').Value;"); + const result = run(directory); + assert.notEqual(result.status, 0); + assert.match(result.output, /index\.d\.ts: missing \.js extension/); +}); + +test('builds extensionless sources and declarations with the packed shared CLI', () => { + const directory = fixture('build'); + const require = createRequire(import.meta.url); + symlinkSync(dirname(require.resolve('tsdown/package.json')), join(directory, 'node_modules/tsdown'), 'dir'); + mkdirSync(join(directory, 'src/nested'), { recursive: true }); + mkdirSync(join(directory, 'dist/css'), { recursive: true }); + writeFileSync(join(directory, 'dist/css/theme.css'), ':root { color: red; }'); + const source = "export { value } from './detail'; export type { Value } from './detail'; export const load = () => import('./nested');"; + writeFileSync(join(directory, 'src/index.ts'), source); + writeFileSync(join(directory, 'src/detail.ts'), 'export type Value = number; export const value: Value = 42;'); + writeFileSync(join(directory, 'src/nested/index.ts'), 'export const nested = true;'); + writeFileSync(join(directory, 'src/native.ts'), 'export const native = true;'); + writeFileSync(join(directory, 'src/unused.spec.ts'), 'throw new Error("Tests must not be built");'); + writeFileSync( + join(directory, 'tsconfig.json'), + JSON.stringify({ compilerOptions: { target: 'ES2020', module: 'ESNext', moduleResolution: 'bundler', strict: true } }), + ); + const result = spawnSync(process.execPath, [buildCli, directory], { cwd: temporary, encoding: 'utf8', timeout: 30000 }); + assert.ifError(result.error); + assert.equal(result.status, 0, result.stdout + result.stderr); + assert.equal(readFileSync(join(directory, 'src/index.ts'), 'utf8'), source); + assert.ok(existsSync(join(directory, 'dist/css/theme.css'))); + assert.ok(!existsSync(join(directory, 'dist/unused.spec.js'))); + assert.match(readFileSync(join(directory, 'dist/index.d.ts'), 'utf8'), /\.\/detail\.js/); + const checked = run(directory); + assert.equal(checked.status, 0, checked.output); + execFileSync( + process.execPath, + [ + '--input-type=module', + '-e', + "const m = await import('theme-build'); if (m.value !== 42 || !(await m.load()).nested) process.exit(1);", + ], + { cwd: directory }, + ); + writeFileSync( + join(directory, 'consumer.mts'), + "import { value, type Value } from 'theme-build'; const result: Value = value; void result;", + ); + execFileSync( + process.execPath, + [require.resolve('typescript/bin/tsc'), '--noEmit', '--module', 'NodeNext', '--target', 'ES2020', 'consumer.mts'], + { cwd: directory }, + ); +}); diff --git a/scripts/theme-build.config.mjs b/scripts/theme-build.config.mjs new file mode 100644 index 0000000..bb330aa --- /dev/null +++ b/scripts/theme-build.config.mjs @@ -0,0 +1,16 @@ +import process from 'node:process'; + +export default { + cwd: process.cwd(), + entry: ['src/**/*.ts', '!src/**/*.spec.ts', '!src/**/*.test.ts', '!src/**/*.d.ts'], + root: 'src', + outDir: 'dist', + format: 'esm', + platform: 'neutral', + target: 'es2020', + unbundle: true, + // CSS is built into dist/css before the JavaScript build in theme packages. + clean: false, + dts: { sourcemap: true }, + deps: { neverBundle: true }, +}; From 3ea7ed1e256e6023d0c738752c08636a63c05d50 Mon Sep 17 00:00:00 2001 From: rdlabo Date: Mon, 28 Sep 2026 21:06:23 +0900 Subject: [PATCH 3/3] chore: release shared build tooling as 0.1.2 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 14e2289..2e7c870 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@rdlabo/ionic-theme-utils", - "version": "0.1.1", + "version": "0.1.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@rdlabo/ionic-theme-utils", - "version": "0.1.1", + "version": "0.1.2", "license": "MIT", "bin": { "rdlabo-build-theme": "scripts/build-theme.mjs", diff --git a/package.json b/package.json index 7a6a6ec..4fab0e6 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@rdlabo/ionic-theme-utils", "private": false, - "version": "0.1.1", + "version": "0.1.2", "description": "Shared utilities for Rdlabo Ionic themes", "type": "module", "main": "./dist/index.js",