Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
13 changes: 13 additions & 0 deletions .changeset/build-cli-raw-imports.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@geonovum/standards-checker': minor
---

Support Vite-style `?raw` raw-text imports in CLI builds. `build-cli` now drives the
tsdown JS API with the standard CLI-build options plus a plugin that resolves and
inlines `*?raw` imports (rolldown doesn't implement the suffix natively), and the
`client` types declare `*?raw` modules as strings. Consumers can import example
fixtures (e.g. YAML files) as raw text without a local tsdown config or ambient
module declaration — remove those shims when upgrading, or the duplicate `*?raw`
declaration will conflict. Note: `build-cli` now takes entries only
(`build-cli <entry> [...entry]`), no longer forwards other CLI flags to tsdown, and
never loads a consumer `tsdown.config.ts`.
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,15 @@ One package, many subpaths:
| `@geonovum/standards-checker/prettier` | Shared Prettier config (use via `"prettier":` field) |
| `@geonovum/standards-checker/tsconfig.app.json` | Base TS config for app source |
| `@geonovum/standards-checker/tsconfig.node.json` | Base TS config for Node-side scripts (`vite.config.ts`) |
| `@geonovum/standards-checker/client` | `*.css` module declaration for TS |
| `@geonovum/standards-checker/client` | `*.css` and `*?raw` module declarations for TS |
| `@geonovum/standards-checker/vitest-client` | Vitest matcher type augmentation (`toContainViolation`) |
| `@geonovum/standards-checker/index.css` | Pre-built CSS (Tailwind compiled at build time) |

Bin shipped with the package: `build-cli` — wraps `tsdown` with the standard CLI-build flags. `vite` and `vitest` are peer deps that live in the consumer's tree; scripts invoke their native bins directly.
Bin shipped with the package: `build-cli` — drives the `tsdown` JS API with the standard CLI-build
options plus a plugin implementing Vite-style `?raw` raw-text imports (rolldown doesn't support the
suffix natively). Usage is `build-cli <entry> [...entry]`; the build is fully standard — a consumer
`tsdown.config.ts` is not loaded. `vite` and `vitest` are peer deps that live in the consumer's
tree; scripts invoke their native bins directly.

---

Expand Down
60 changes: 49 additions & 11 deletions bin/build-cli.mjs
Original file line number Diff line number Diff line change
@@ -1,16 +1,54 @@
#!/usr/bin/env node
import { spawn } from 'node:child_process';
import { realpathSync } from 'node:fs';
import { readFileSync, realpathSync } from 'node:fs';
import { createRequire } from 'node:module';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { dirname, resolve } from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';

// Resolve tsdown from this package's own dependencies, wherever the bin shim
// lives in the consumer's tree.
const require = createRequire(realpathSync(fileURLToPath(import.meta.url)));
const tsdownEntry = require.resolve('tsdown');
const tsdownRoot = tsdownEntry.match(/^(.+[/\\]tsdown)[/\\]/)[1];
const bin = join(tsdownRoot, 'dist', 'run.mjs');
const { build } = await import(pathToFileURL(require.resolve('tsdown')).href);

const userArgs = process.argv.slice(2);
const args = [bin, ...userArgs, '--format', 'esm', '--platform', 'node', '--sourcemap', '--out-dir', 'dist', '--clean'];
const child = spawn(process.execPath, args, { stdio: 'inherit' });
child.on('exit', code => process.exit(code ?? 1));
const RAW_QUERY = /\?raw$/;

// Rolldown doesn't implement Vite's `?raw` suffix imports (raw file content as
// a string), which consumer apps use for example fixtures; resolve and inline
// them here so CLI bundles behave like the Vite-built webapp.
const rawImports = {
name: 'raw-imports',
resolveId(source, importer) {
if (!RAW_QUERY.test(source) || !importer) return null;
return `${resolve(dirname(importer), source.replace(RAW_QUERY, ''))}?raw`;
},
load(id) {
if (!RAW_QUERY.test(id)) return null;
return { code: `export default ${JSON.stringify(readFileSync(id.replace(RAW_QUERY, ''), 'utf8'))};`, moduleType: 'js' };
},
};

const entry = process.argv.slice(2);
if (entry.length === 0 || entry.some(arg => arg.startsWith('-'))) {
console.error('Usage: build-cli <entry> [...entry]');
process.exit(1);
}

// The tsdown JS API instead of its CLI: everything stays relative to the
// consumer's cwd, and the raw-imports plugin can be passed directly (the CLI
// only takes plugins via a config file, which would shift relative paths to
// the config's directory). `config: false` keeps the build fully standard —
// a consumer tsdown.config.ts is not loaded.
try {
await build({
entry,
config: false,
format: 'esm',
platform: 'node',
sourcemap: true,
outDir: 'dist',
clean: true,
plugins: [rawImports],
});
} catch (error) {
console.error(error);
process.exit(1);
}
7 changes: 7 additions & 0 deletions client.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
declare module '*.css' {}

// Vite's `?raw` suffix imports a file's content as a string (used for example
// fixtures); `build-cli` implements the same behavior for CLI bundles.
declare module '*?raw' {
const content: string;
export default content;
}