@celox-sim/vite-plugin 0.0.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,18 @@
1
+ import type { GenTsJsonOutput } from "./types.js";
2
+ export declare class GenTsCache {
3
+ private readonly _projectRoot;
4
+ private _data;
5
+ private _mtimeKey;
6
+ constructor(_projectRoot: string);
7
+ /** Get cached data, refreshing if any .veryl file has changed. */
8
+ get(): GenTsJsonOutput;
9
+ /** Force invalidation — next `get()` will re-run the generator. */
10
+ invalidate(): void;
11
+ /**
12
+ * Build a key from the max mtime of all .veryl files in the project.
13
+ * This is intentionally cheap — only stats, no file reads.
14
+ */
15
+ private computeMtimeKey;
16
+ private walkVerylFiles;
17
+ }
18
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlD,qBAAa,UAAU;IAIT,OAAO,CAAC,QAAQ,CAAC,YAAY;IAHzC,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,SAAS,CAAM;gBAEM,YAAY,EAAE,MAAM;IAEjD,kEAAkE;IAClE,GAAG,IAAI,eAAe;IAUtB,mEAAmE;IACnE,UAAU,IAAI,IAAI;IAKlB;;;OAGG;IACH,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,cAAc;CAgCvB"}
package/dist/cache.js ADDED
@@ -0,0 +1,71 @@
1
+ import { readdirSync, statSync } from "node:fs";
2
+ import { join, extname } from "node:path";
3
+ import { runGenTs } from "./generator.js";
4
+ export class GenTsCache {
5
+ _projectRoot;
6
+ _data;
7
+ _mtimeKey = "";
8
+ constructor(_projectRoot) {
9
+ this._projectRoot = _projectRoot;
10
+ }
11
+ /** Get cached data, refreshing if any .veryl file has changed. */
12
+ get() {
13
+ const key = this.computeMtimeKey();
14
+ if (this._data && this._mtimeKey === key) {
15
+ return this._data;
16
+ }
17
+ this._data = runGenTs(this._projectRoot);
18
+ this._mtimeKey = key;
19
+ return this._data;
20
+ }
21
+ /** Force invalidation — next `get()` will re-run the generator. */
22
+ invalidate() {
23
+ this._mtimeKey = "";
24
+ this._data = undefined;
25
+ }
26
+ /**
27
+ * Build a key from the max mtime of all .veryl files in the project.
28
+ * This is intentionally cheap — only stats, no file reads.
29
+ */
30
+ computeMtimeKey() {
31
+ let maxMtime = 0;
32
+ let count = 0;
33
+ this.walkVerylFiles(this._projectRoot, (mtimeMs) => {
34
+ if (mtimeMs > maxMtime)
35
+ maxMtime = mtimeMs;
36
+ count++;
37
+ });
38
+ return `${count}:${maxMtime}`;
39
+ }
40
+ walkVerylFiles(dir, cb) {
41
+ let entries;
42
+ try {
43
+ entries = readdirSync(dir, { withFileTypes: true });
44
+ }
45
+ catch {
46
+ return;
47
+ }
48
+ for (const entry of entries) {
49
+ const full = join(dir, entry.name);
50
+ if (entry.isDirectory()) {
51
+ // Skip common non-source directories
52
+ if (entry.name === "node_modules" ||
53
+ entry.name === "target" ||
54
+ entry.name === ".git" ||
55
+ entry.name === "dist") {
56
+ continue;
57
+ }
58
+ this.walkVerylFiles(full, cb);
59
+ }
60
+ else if (extname(entry.name) === ".veryl") {
61
+ try {
62
+ cb(statSync(full).mtimeMs);
63
+ }
64
+ catch {
65
+ // file may have been deleted
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../src/cache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE1C,MAAM,OAAO,UAAU;IAIQ;IAHrB,KAAK,CAA8B;IACnC,SAAS,GAAG,EAAE,CAAC;IAEvB,YAA6B,YAAoB;QAApB,iBAAY,GAAZ,YAAY,CAAQ;IAAG,CAAC;IAErD,kEAAkE;IAClE,GAAG;QACD,MAAM,GAAG,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;QACnC,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,SAAS,KAAK,GAAG,EAAE,CAAC;YACzC,OAAO,IAAI,CAAC,KAAK,CAAC;QACpB,CAAC;QACD,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QACzC,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC;QACrB,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,mEAAmE;IACnE,UAAU;QACR,IAAI,CAAC,SAAS,GAAG,EAAE,CAAC;QACpB,IAAI,CAAC,KAAK,GAAG,SAAS,CAAC;IACzB,CAAC;IAED;;;OAGG;IACK,eAAe;QACrB,IAAI,QAAQ,GAAG,CAAC,CAAC;QACjB,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC,OAAO,EAAE,EAAE;YACjD,IAAI,OAAO,GAAG,QAAQ;gBAAE,QAAQ,GAAG,OAAO,CAAC;YAC3C,KAAK,EAAE,CAAC;QACV,CAAC,CAAC,CAAC;QACH,OAAO,GAAG,KAAK,IAAI,QAAQ,EAAE,CAAC;IAChC,CAAC;IAEO,cAAc,CACpB,GAAW,EACX,EAA6B;QAE7B,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,CAAC;QAAC,MAAM,CAAC;YACP,OAAO;QACT,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YACnC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;gBACxB,qCAAqC;gBACrC,IACE,KAAK,CAAC,IAAI,KAAK,cAAc;oBAC7B,KAAK,CAAC,IAAI,KAAK,QAAQ;oBACvB,KAAK,CAAC,IAAI,KAAK,MAAM;oBACrB,KAAK,CAAC,IAAI,KAAK,MAAM,EACrB,CAAC;oBACD,SAAS;gBACX,CAAC;gBACD,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAChC,CAAC;iBAAM,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,QAAQ,EAAE,CAAC;gBAC5C,IAAI,CAAC;oBACH,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC;gBAC7B,CAAC;gBAAC,MAAM,CAAC;oBACP,6BAA6B;gBAC/B,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,6 @@
1
+ import type { GenTsJsonOutput } from "./types.js";
2
+ /**
3
+ * Run the TypeScript type generator via the NAPI addon and parse the output.
4
+ */
5
+ export declare function runGenTs(projectRoot: string): GenTsJsonOutput;
6
+ //# sourceMappingURL=generator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD;;GAEG;AACH,wBAAgB,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe,CAI7D"}
@@ -0,0 +1,10 @@
1
+ import { loadNativeAddon } from "@celox-sim/celox";
2
+ /**
3
+ * Run the TypeScript type generator via the NAPI addon and parse the output.
4
+ */
5
+ export function runGenTs(projectRoot) {
6
+ const addon = loadNativeAddon();
7
+ const json = addon.genTs(projectRoot);
8
+ return JSON.parse(json);
9
+ }
10
+ //# sourceMappingURL=generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../src/generator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGnD;;GAEG;AACH,MAAM,UAAU,QAAQ,CAAC,WAAmB;IAC1C,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;IAChC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;IACtC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAoB,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { Plugin } from "vite";
2
+ import type { CeloxPluginOptions } from "./types.js";
3
+ export type { CeloxPluginOptions } from "./types.js";
4
+ /**
5
+ * Vite plugin for importing `.veryl` files as typed `ModuleDefinition` objects.
6
+ *
7
+ * ```ts
8
+ * // vitest.config.ts
9
+ * import celox from "@celox-sim/vite-plugin";
10
+ * export default defineConfig({ plugins: [celox()] });
11
+ *
12
+ * // test file
13
+ * import { Adder } from './src/Adder.veryl';
14
+ * const sim = Simulator.create(Adder); // fully typed
15
+ * ```
16
+ */
17
+ export default function celoxPlugin(options?: CeloxPluginOptions): Plugin;
18
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAO,KAAK,EAAE,kBAAkB,EAAe,MAAM,YAAY,CAAC;AAIlE,YAAY,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAIrD;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,OAAO,UAAU,WAAW,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAsFxE"}
package/dist/index.js ADDED
@@ -0,0 +1,152 @@
1
+ import { resolve, isAbsolute, dirname } from "node:path";
2
+ import { existsSync } from "node:fs";
3
+ import { GenTsCache } from "./cache.js";
4
+ import { generateSidecars, cleanSidecars } from "./sidecar.js";
5
+ const VERYL_PREFIX = "\0veryl:";
6
+ /**
7
+ * Vite plugin for importing `.veryl` files as typed `ModuleDefinition` objects.
8
+ *
9
+ * ```ts
10
+ * // vitest.config.ts
11
+ * import celox from "@celox-sim/vite-plugin";
12
+ * export default defineConfig({ plugins: [celox()] });
13
+ *
14
+ * // test file
15
+ * import { Adder } from './src/Adder.veryl';
16
+ * const sim = Simulator.create(Adder); // fully typed
17
+ * ```
18
+ */
19
+ export default function celoxPlugin(options) {
20
+ let projectRoot;
21
+ let cache;
22
+ let sidecarPaths = [];
23
+ return {
24
+ name: "vite-plugin-celox",
25
+ enforce: "pre",
26
+ configResolved(config) {
27
+ // Determine project root
28
+ if (options?.projectRoot) {
29
+ projectRoot = resolve(options.projectRoot);
30
+ }
31
+ else {
32
+ projectRoot = findVerylProjectRoot(config.root);
33
+ }
34
+ cache = new GenTsCache(projectRoot);
35
+ },
36
+ buildStart() {
37
+ // Run generator and create type sidecars
38
+ const data = cache.get();
39
+ cleanSidecars(sidecarPaths);
40
+ sidecarPaths = generateSidecars(data, projectRoot);
41
+ },
42
+ resolveId(source, importer) {
43
+ if (!source.endsWith(".veryl"))
44
+ return;
45
+ // Resolve to absolute path
46
+ let absPath;
47
+ if (isAbsolute(source)) {
48
+ absPath = source;
49
+ }
50
+ else if (importer) {
51
+ absPath = resolve(dirname(importer), source);
52
+ }
53
+ else {
54
+ absPath = resolve(source);
55
+ }
56
+ // Only handle .veryl files that exist
57
+ if (!existsSync(absPath))
58
+ return;
59
+ return VERYL_PREFIX + absPath;
60
+ },
61
+ load(id) {
62
+ if (!id.startsWith(VERYL_PREFIX))
63
+ return;
64
+ const absPath = id.slice(VERYL_PREFIX.length);
65
+ const data = cache.get();
66
+ // Find the relative source file path
67
+ const relPath = makeRelative(absPath, projectRoot);
68
+ const moduleNames = data.fileModules[relPath];
69
+ if (!moduleNames || moduleNames.length === 0) {
70
+ this.warn(`No modules found in ${relPath}`);
71
+ return "export {};";
72
+ }
73
+ // Build ESM exports for each module in this file
74
+ const exports = moduleNames
75
+ .map((name) => {
76
+ const mod = data.modules.find((m) => m.moduleName === name);
77
+ if (!mod)
78
+ return "";
79
+ return generateEsmExport(mod, data.projectPath);
80
+ })
81
+ .filter((s) => s.length > 0)
82
+ .join("\n\n");
83
+ return exports;
84
+ },
85
+ handleHotUpdate({ file }) {
86
+ if (!file.endsWith(".veryl"))
87
+ return;
88
+ // Invalidate cache so next load re-runs the generator
89
+ cache.invalidate();
90
+ // Re-generate sidecars
91
+ const data = cache.get();
92
+ cleanSidecars(sidecarPaths);
93
+ sidecarPaths = generateSidecars(data, projectRoot);
94
+ },
95
+ };
96
+ }
97
+ /**
98
+ * Search upward from `startDir` for a directory containing `Veryl.toml`.
99
+ */
100
+ function findVerylProjectRoot(startDir) {
101
+ let dir = resolve(startDir);
102
+ // eslint-disable-next-line no-constant-condition
103
+ while (true) {
104
+ if (existsSync(resolve(dir, "Veryl.toml"))) {
105
+ return dir;
106
+ }
107
+ const parent = dirname(dir);
108
+ if (parent === dir) {
109
+ throw new Error("Could not find Veryl.toml in any parent directory. " +
110
+ "Set the projectRoot plugin option explicitly.");
111
+ }
112
+ dir = parent;
113
+ }
114
+ }
115
+ /**
116
+ * Make `absPath` relative to `base`, using forward slashes.
117
+ */
118
+ function makeRelative(absPath, base) {
119
+ // Normalise to forward slashes so the comparison works on Windows
120
+ const normAbs = absPath.replace(/\\/g, "/");
121
+ const normBase = base.replace(/\\/g, "/").replace(/\/$/, "");
122
+ let rel = normAbs;
123
+ if (normAbs.startsWith(normBase + "/")) {
124
+ rel = normAbs.slice(normBase.length + 1);
125
+ }
126
+ else if (normAbs.startsWith(normBase)) {
127
+ rel = normAbs.slice(normBase.length);
128
+ }
129
+ if (rel.startsWith("/")) {
130
+ rel = rel.slice(1);
131
+ }
132
+ return rel;
133
+ }
134
+ /**
135
+ * Generate an ESM export for a single module.
136
+ */
137
+ function generateEsmExport(mod, projectPath) {
138
+ const portsJson = JSON.stringify(mod.ports, null, 2)
139
+ .split("\n")
140
+ .map((line, i) => (i === 0 ? line : " " + line))
141
+ .join("\n");
142
+ const eventsJson = JSON.stringify(mod.events);
143
+ return `export const ${mod.moduleName} = {
144
+ __celox_module: true,
145
+ name: ${JSON.stringify(mod.moduleName)},
146
+ source: "",
147
+ projectPath: ${JSON.stringify(projectPath)},
148
+ ports: ${portsJson},
149
+ events: ${eventsJson},
150
+ };`;
151
+ }
152
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { GenTsJsonOutput } from "./types.js";
2
+ /** Default directory (relative to project root) for generated sidecar files. */
3
+ export declare const SIDECAR_DIR = ".celox";
4
+ /**
5
+ * Generate `.d.veryl.ts` sidecar files in the sidecar directory,
6
+ * mirroring the source tree structure.
7
+ *
8
+ * TypeScript picks these up via `allowArbitraryExtensions` + `rootDirs`
9
+ * in tsconfig. For example, `src/Adder.veryl` produces
10
+ * `.celox/src/Adder.d.veryl.ts`.
11
+ */
12
+ export declare function generateSidecars(data: GenTsJsonOutput, projectRoot: string): string[];
13
+ /**
14
+ * Remove all sidecar files that were previously generated.
15
+ */
16
+ export declare function cleanSidecars(paths: string[]): void;
17
+ //# sourceMappingURL=sidecar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidecar.d.ts","sourceRoot":"","sources":["../src/sidecar.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,gFAAgF;AAChF,eAAO,MAAM,WAAW,WAAW,CAAC;AAEpC;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,eAAe,EACrB,WAAW,EAAE,MAAM,GAClB,MAAM,EAAE,CAkCV;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAUnD"}
@@ -0,0 +1,68 @@
1
+ import { writeFileSync, mkdirSync, unlinkSync, existsSync } from "node:fs";
2
+ import { join, resolve, dirname, relative, isAbsolute } from "node:path";
3
+ /** Default directory (relative to project root) for generated sidecar files. */
4
+ export const SIDECAR_DIR = ".celox";
5
+ /**
6
+ * Generate `.d.veryl.ts` sidecar files in the sidecar directory,
7
+ * mirroring the source tree structure.
8
+ *
9
+ * TypeScript picks these up via `allowArbitraryExtensions` + `rootDirs`
10
+ * in tsconfig. For example, `src/Adder.veryl` produces
11
+ * `.celox/src/Adder.d.veryl.ts`.
12
+ */
13
+ export function generateSidecars(data, projectRoot) {
14
+ const written = [];
15
+ const root = resolve(projectRoot);
16
+ const outDir = join(root, SIDECAR_DIR);
17
+ for (const [sourceFile, moduleNames] of Object.entries(data.fileModules)) {
18
+ const verylPath = resolve(projectRoot, sourceFile);
19
+ // Skip files that don't exist on disk (e.g. standard library paths
20
+ // reported with a stripped leading "/" by celox-gen-ts)
21
+ if (!existsSync(verylPath))
22
+ continue;
23
+ const rel = relative(root, verylPath);
24
+ // Skip files outside the project root (e.g. standard library from global
25
+ // cache). On Windows, relative() across different drives returns an
26
+ // absolute path; on all platforms, paths outside root start with "..".
27
+ if (isAbsolute(rel) || rel.startsWith(".."))
28
+ continue;
29
+ const sidecarPath = sidecarPathFor(join(outDir, rel));
30
+ const modules = moduleNames
31
+ .map((name) => data.modules.find((m) => m.moduleName === name))
32
+ .filter((m) => m !== undefined);
33
+ if (modules.length === 0)
34
+ continue;
35
+ // Combine dtsContent from all modules in this file
36
+ // Each module's dtsContent already contains the import and interface
37
+ const content = modules.map((m) => m.dtsContent).join("\n");
38
+ mkdirSync(dirname(sidecarPath), { recursive: true });
39
+ writeFileSync(sidecarPath, content, "utf-8");
40
+ written.push(sidecarPath);
41
+ }
42
+ return written;
43
+ }
44
+ /**
45
+ * Remove all sidecar files that were previously generated.
46
+ */
47
+ export function cleanSidecars(paths) {
48
+ for (const p of paths) {
49
+ try {
50
+ if (existsSync(p)) {
51
+ unlinkSync(p);
52
+ }
53
+ }
54
+ catch {
55
+ // ignore cleanup errors
56
+ }
57
+ }
58
+ }
59
+ /**
60
+ * Given `/path/to/.celox/src/Adder.veryl`, returns `/path/to/.celox/src/Adder.d.veryl.ts`.
61
+ */
62
+ function sidecarPathFor(verylPath) {
63
+ const dir = dirname(verylPath);
64
+ const base = verylPath.slice(dir.length + 1); // "Adder.veryl"
65
+ const stem = base.replace(/\.veryl$/, "");
66
+ return join(dir, `${stem}.d.veryl.ts`);
67
+ }
68
+ //# sourceMappingURL=sidecar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidecar.js","sourceRoot":"","sources":["../src/sidecar.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /** JSON output from `celox-gen-ts --json`. */
2
+ export interface GenTsJsonOutput {
3
+ readonly projectPath: string;
4
+ readonly modules: readonly GenTsModule[];
5
+ readonly fileModules: Record<string, readonly string[]>;
6
+ }
7
+ /** Per-module entry from `celox-gen-ts --json`. */
8
+ export interface GenTsModule {
9
+ readonly moduleName: string;
10
+ readonly sourceFile: string;
11
+ readonly dtsContent: string;
12
+ readonly mdContent: string;
13
+ readonly ports: Record<string, GenTsPortInfo>;
14
+ readonly events: readonly string[];
15
+ }
16
+ /** Port metadata from the generator. */
17
+ export interface GenTsPortInfo {
18
+ readonly direction: "input" | "output" | "inout";
19
+ readonly type: "clock" | "reset" | "logic" | "bit";
20
+ readonly width: number;
21
+ readonly is4state: boolean;
22
+ }
23
+ /** Plugin options. */
24
+ export interface CeloxPluginOptions {
25
+ /** Explicit path to the Veryl project root (directory containing Veryl.toml). */
26
+ projectRoot?: string;
27
+ }
28
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAC9C,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,CAAC;IACzC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;CACzD;AAED,mDAAmD;AACnD,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;IAC9C,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,wCAAwC;AACxC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;IACjD,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,CAAC;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;CAC5B;AAED,sBAAsB;AACtB,MAAM,WAAW,kBAAkB;IACjC,iFAAiF;IACjF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,10 +1,46 @@
1
1
  {
2
2
  "name": "@celox-sim/vite-plugin",
3
- "version": "0.0.1",
4
- "description": "OIDC trusted publishing setup package for @celox-sim/vite-plugin",
3
+ "version": "0.1.3",
4
+ "description": "Vite plugin for importing .veryl files as typed ModuleDefinition objects",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "src"
17
+ ],
5
18
  "keywords": [
6
- "oidc",
7
- "trusted-publishing",
8
- "setup"
9
- ]
10
- }
19
+ "celox",
20
+ "veryl",
21
+ "hdl",
22
+ "vite",
23
+ "vitest",
24
+ "plugin"
25
+ ],
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "https://github.com/celox-sim/celox",
29
+ "directory": "packages/vite-plugin"
30
+ },
31
+ "license": "MIT",
32
+ "peerDependencies": {
33
+ "vite": ">=5.0.0"
34
+ },
35
+ "dependencies": {
36
+ "@celox-sim/celox": "0.1.3"
37
+ },
38
+ "devDependencies": {
39
+ "@types/node": "^25.3.2",
40
+ "typescript": "^5.7.0",
41
+ "vite": "^6.0.0"
42
+ },
43
+ "scripts": {
44
+ "build": "tsc"
45
+ }
46
+ }
package/src/cache.ts ADDED
@@ -0,0 +1,75 @@
1
+ import { readdirSync, statSync } from "node:fs";
2
+ import { join, extname } from "node:path";
3
+ import type { GenTsJsonOutput } from "./types.js";
4
+ import { runGenTs } from "./generator.js";
5
+
6
+ export class GenTsCache {
7
+ private _data: GenTsJsonOutput | undefined;
8
+ private _mtimeKey = "";
9
+
10
+ constructor(private readonly _projectRoot: string) {}
11
+
12
+ /** Get cached data, refreshing if any .veryl file has changed. */
13
+ get(): GenTsJsonOutput {
14
+ const key = this.computeMtimeKey();
15
+ if (this._data && this._mtimeKey === key) {
16
+ return this._data;
17
+ }
18
+ this._data = runGenTs(this._projectRoot);
19
+ this._mtimeKey = key;
20
+ return this._data;
21
+ }
22
+
23
+ /** Force invalidation — next `get()` will re-run the generator. */
24
+ invalidate(): void {
25
+ this._mtimeKey = "";
26
+ this._data = undefined;
27
+ }
28
+
29
+ /**
30
+ * Build a key from the max mtime of all .veryl files in the project.
31
+ * This is intentionally cheap — only stats, no file reads.
32
+ */
33
+ private computeMtimeKey(): string {
34
+ let maxMtime = 0;
35
+ let count = 0;
36
+ this.walkVerylFiles(this._projectRoot, (mtimeMs) => {
37
+ if (mtimeMs > maxMtime) maxMtime = mtimeMs;
38
+ count++;
39
+ });
40
+ return `${count}:${maxMtime}`;
41
+ }
42
+
43
+ private walkVerylFiles(
44
+ dir: string,
45
+ cb: (mtimeMs: number) => void,
46
+ ): void {
47
+ let entries;
48
+ try {
49
+ entries = readdirSync(dir, { withFileTypes: true });
50
+ } catch {
51
+ return;
52
+ }
53
+ for (const entry of entries) {
54
+ const full = join(dir, entry.name);
55
+ if (entry.isDirectory()) {
56
+ // Skip common non-source directories
57
+ if (
58
+ entry.name === "node_modules" ||
59
+ entry.name === "target" ||
60
+ entry.name === ".git" ||
61
+ entry.name === "dist"
62
+ ) {
63
+ continue;
64
+ }
65
+ this.walkVerylFiles(full, cb);
66
+ } else if (extname(entry.name) === ".veryl") {
67
+ try {
68
+ cb(statSync(full).mtimeMs);
69
+ } catch {
70
+ // file may have been deleted
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,11 @@
1
+ import { loadNativeAddon } from "@celox-sim/celox";
2
+ import type { GenTsJsonOutput } from "./types.js";
3
+
4
+ /**
5
+ * Run the TypeScript type generator via the NAPI addon and parse the output.
6
+ */
7
+ export function runGenTs(projectRoot: string): GenTsJsonOutput {
8
+ const addon = loadNativeAddon();
9
+ const json = addon.genTs(projectRoot);
10
+ return JSON.parse(json) as GenTsJsonOutput;
11
+ }
package/src/index.ts ADDED
@@ -0,0 +1,172 @@
1
+ import { resolve, isAbsolute, dirname } from "node:path";
2
+ import { existsSync } from "node:fs";
3
+ import type { Plugin } from "vite";
4
+ import type { CeloxPluginOptions, GenTsModule } from "./types.js";
5
+ import { GenTsCache } from "./cache.js";
6
+ import { generateSidecars, cleanSidecars } from "./sidecar.js";
7
+
8
+ export type { CeloxPluginOptions } from "./types.js";
9
+
10
+ const VERYL_PREFIX = "\0veryl:";
11
+
12
+ /**
13
+ * Vite plugin for importing `.veryl` files as typed `ModuleDefinition` objects.
14
+ *
15
+ * ```ts
16
+ * // vitest.config.ts
17
+ * import celox from "@celox-sim/vite-plugin";
18
+ * export default defineConfig({ plugins: [celox()] });
19
+ *
20
+ * // test file
21
+ * import { Adder } from './src/Adder.veryl';
22
+ * const sim = Simulator.create(Adder); // fully typed
23
+ * ```
24
+ */
25
+ export default function celoxPlugin(options?: CeloxPluginOptions): Plugin {
26
+ let projectRoot: string;
27
+ let cache: GenTsCache;
28
+ let sidecarPaths: string[] = [];
29
+
30
+ return {
31
+ name: "vite-plugin-celox",
32
+ enforce: "pre",
33
+
34
+ configResolved(config) {
35
+ // Determine project root
36
+ if (options?.projectRoot) {
37
+ projectRoot = resolve(options.projectRoot);
38
+ } else {
39
+ projectRoot = findVerylProjectRoot(config.root);
40
+ }
41
+
42
+ cache = new GenTsCache(projectRoot);
43
+ },
44
+
45
+ buildStart() {
46
+ // Run generator and create type sidecars
47
+ const data = cache.get();
48
+ cleanSidecars(sidecarPaths);
49
+ sidecarPaths = generateSidecars(data, projectRoot);
50
+ },
51
+
52
+ resolveId(source, importer) {
53
+ if (!source.endsWith(".veryl")) return;
54
+
55
+ // Resolve to absolute path
56
+ let absPath: string;
57
+ if (isAbsolute(source)) {
58
+ absPath = source;
59
+ } else if (importer) {
60
+ absPath = resolve(dirname(importer), source);
61
+ } else {
62
+ absPath = resolve(source);
63
+ }
64
+
65
+ // Only handle .veryl files that exist
66
+ if (!existsSync(absPath)) return;
67
+
68
+ return VERYL_PREFIX + absPath;
69
+ },
70
+
71
+ load(id) {
72
+ if (!id.startsWith(VERYL_PREFIX)) return;
73
+
74
+ const absPath = id.slice(VERYL_PREFIX.length);
75
+ const data = cache.get();
76
+
77
+ // Find the relative source file path
78
+ const relPath = makeRelative(absPath, projectRoot);
79
+ const moduleNames = data.fileModules[relPath];
80
+
81
+ if (!moduleNames || moduleNames.length === 0) {
82
+ this.warn(`No modules found in ${relPath}`);
83
+ return "export {};";
84
+ }
85
+
86
+ // Build ESM exports for each module in this file
87
+ const exports = moduleNames
88
+ .map((name) => {
89
+ const mod = data.modules.find((m) => m.moduleName === name);
90
+ if (!mod) return "";
91
+ return generateEsmExport(mod, data.projectPath);
92
+ })
93
+ .filter((s) => s.length > 0)
94
+ .join("\n\n");
95
+
96
+ return exports;
97
+ },
98
+
99
+ handleHotUpdate({ file }) {
100
+ if (!file.endsWith(".veryl")) return;
101
+
102
+ // Invalidate cache so next load re-runs the generator
103
+ cache.invalidate();
104
+
105
+ // Re-generate sidecars
106
+ const data = cache.get();
107
+ cleanSidecars(sidecarPaths);
108
+ sidecarPaths = generateSidecars(data, projectRoot);
109
+ },
110
+ };
111
+ }
112
+
113
+ /**
114
+ * Search upward from `startDir` for a directory containing `Veryl.toml`.
115
+ */
116
+ function findVerylProjectRoot(startDir: string): string {
117
+ let dir = resolve(startDir);
118
+ // eslint-disable-next-line no-constant-condition
119
+ while (true) {
120
+ if (existsSync(resolve(dir, "Veryl.toml"))) {
121
+ return dir;
122
+ }
123
+ const parent = dirname(dir);
124
+ if (parent === dir) {
125
+ throw new Error(
126
+ "Could not find Veryl.toml in any parent directory. " +
127
+ "Set the projectRoot plugin option explicitly.",
128
+ );
129
+ }
130
+ dir = parent;
131
+ }
132
+ }
133
+
134
+ /**
135
+ * Make `absPath` relative to `base`, using forward slashes.
136
+ */
137
+ function makeRelative(absPath: string, base: string): string {
138
+ // Normalise to forward slashes so the comparison works on Windows
139
+ const normAbs = absPath.replace(/\\/g, "/");
140
+ const normBase = base.replace(/\\/g, "/").replace(/\/$/, "");
141
+ let rel = normAbs;
142
+ if (normAbs.startsWith(normBase + "/")) {
143
+ rel = normAbs.slice(normBase.length + 1);
144
+ } else if (normAbs.startsWith(normBase)) {
145
+ rel = normAbs.slice(normBase.length);
146
+ }
147
+ if (rel.startsWith("/")) {
148
+ rel = rel.slice(1);
149
+ }
150
+ return rel;
151
+ }
152
+
153
+ /**
154
+ * Generate an ESM export for a single module.
155
+ */
156
+ function generateEsmExport(mod: GenTsModule, projectPath: string): string {
157
+ const portsJson = JSON.stringify(mod.ports, null, 2)
158
+ .split("\n")
159
+ .map((line, i) => (i === 0 ? line : " " + line))
160
+ .join("\n");
161
+
162
+ const eventsJson = JSON.stringify(mod.events);
163
+
164
+ return `export const ${mod.moduleName} = {
165
+ __celox_module: true,
166
+ name: ${JSON.stringify(mod.moduleName)},
167
+ source: "",
168
+ projectPath: ${JSON.stringify(projectPath)},
169
+ ports: ${portsJson},
170
+ events: ${eventsJson},
171
+ };`;
172
+ }
package/src/sidecar.ts ADDED
@@ -0,0 +1,78 @@
1
+ import { writeFileSync, mkdirSync, unlinkSync, existsSync } from "node:fs";
2
+ import { join, resolve, dirname, relative, isAbsolute } from "node:path";
3
+ import type { GenTsJsonOutput } from "./types.js";
4
+
5
+ /** Default directory (relative to project root) for generated sidecar files. */
6
+ export const SIDECAR_DIR = ".celox";
7
+
8
+ /**
9
+ * Generate `.d.veryl.ts` sidecar files in the sidecar directory,
10
+ * mirroring the source tree structure.
11
+ *
12
+ * TypeScript picks these up via `allowArbitraryExtensions` + `rootDirs`
13
+ * in tsconfig. For example, `src/Adder.veryl` produces
14
+ * `.celox/src/Adder.d.veryl.ts`.
15
+ */
16
+ export function generateSidecars(
17
+ data: GenTsJsonOutput,
18
+ projectRoot: string,
19
+ ): string[] {
20
+ const written: string[] = [];
21
+ const root = resolve(projectRoot);
22
+ const outDir = join(root, SIDECAR_DIR);
23
+
24
+ for (const [sourceFile, moduleNames] of Object.entries(data.fileModules)) {
25
+ const verylPath = resolve(projectRoot, sourceFile);
26
+ // Skip files that don't exist on disk (e.g. standard library paths
27
+ // reported with a stripped leading "/" by celox-gen-ts)
28
+ if (!existsSync(verylPath)) continue;
29
+
30
+ const rel = relative(root, verylPath);
31
+ // Skip files outside the project root (e.g. standard library from global
32
+ // cache). On Windows, relative() across different drives returns an
33
+ // absolute path; on all platforms, paths outside root start with "..".
34
+ if (isAbsolute(rel) || rel.startsWith("..")) continue;
35
+ const sidecarPath = sidecarPathFor(join(outDir, rel));
36
+
37
+ const modules = moduleNames
38
+ .map((name) => data.modules.find((m) => m.moduleName === name))
39
+ .filter((m) => m !== undefined);
40
+
41
+ if (modules.length === 0) continue;
42
+
43
+ // Combine dtsContent from all modules in this file
44
+ // Each module's dtsContent already contains the import and interface
45
+ const content = modules.map((m) => m.dtsContent).join("\n");
46
+
47
+ mkdirSync(dirname(sidecarPath), { recursive: true });
48
+ writeFileSync(sidecarPath, content, "utf-8");
49
+ written.push(sidecarPath);
50
+ }
51
+
52
+ return written;
53
+ }
54
+
55
+ /**
56
+ * Remove all sidecar files that were previously generated.
57
+ */
58
+ export function cleanSidecars(paths: string[]): void {
59
+ for (const p of paths) {
60
+ try {
61
+ if (existsSync(p)) {
62
+ unlinkSync(p);
63
+ }
64
+ } catch {
65
+ // ignore cleanup errors
66
+ }
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Given `/path/to/.celox/src/Adder.veryl`, returns `/path/to/.celox/src/Adder.d.veryl.ts`.
72
+ */
73
+ function sidecarPathFor(verylPath: string): string {
74
+ const dir = dirname(verylPath);
75
+ const base = verylPath.slice(dir.length + 1); // "Adder.veryl"
76
+ const stem = base.replace(/\.veryl$/, "");
77
+ return join(dir, `${stem}.d.veryl.ts`);
78
+ }
package/src/types.ts ADDED
@@ -0,0 +1,30 @@
1
+ /** JSON output from `celox-gen-ts --json`. */
2
+ export interface GenTsJsonOutput {
3
+ readonly projectPath: string;
4
+ readonly modules: readonly GenTsModule[];
5
+ readonly fileModules: Record<string, readonly string[]>;
6
+ }
7
+
8
+ /** Per-module entry from `celox-gen-ts --json`. */
9
+ export interface GenTsModule {
10
+ readonly moduleName: string;
11
+ readonly sourceFile: string;
12
+ readonly dtsContent: string;
13
+ readonly mdContent: string;
14
+ readonly ports: Record<string, GenTsPortInfo>;
15
+ readonly events: readonly string[];
16
+ }
17
+
18
+ /** Port metadata from the generator. */
19
+ export interface GenTsPortInfo {
20
+ readonly direction: "input" | "output" | "inout";
21
+ readonly type: "clock" | "reset" | "logic" | "bit";
22
+ readonly width: number;
23
+ readonly is4state: boolean;
24
+ }
25
+
26
+ /** Plugin options. */
27
+ export interface CeloxPluginOptions {
28
+ /** Explicit path to the Veryl project root (directory containing Veryl.toml). */
29
+ projectRoot?: string;
30
+ }
package/README.md DELETED
@@ -1,45 +0,0 @@
1
- # @celox-sim/vite-plugin
2
-
3
- ## ⚠️ IMPORTANT NOTICE ⚠️
4
-
5
- **This package is created solely for the purpose of setting up OIDC (OpenID Connect) trusted publishing with npm.**
6
-
7
- This is **NOT** a functional package and contains **NO** code or functionality beyond the OIDC setup configuration.
8
-
9
- ## Purpose
10
-
11
- This package exists to:
12
- 1. Configure OIDC trusted publishing for the package name `@celox-sim/vite-plugin`
13
- 2. Enable secure, token-less publishing from CI/CD workflows
14
- 3. Establish provenance for packages published under this name
15
-
16
- ## What is OIDC Trusted Publishing?
17
-
18
- OIDC trusted publishing allows package maintainers to publish packages directly from their CI/CD workflows without needing to manage npm access tokens. Instead, it uses OpenID Connect to establish trust between the CI/CD provider (like GitHub Actions) and npm.
19
-
20
- ## Setup Instructions
21
-
22
- To properly configure OIDC trusted publishing for this package:
23
-
24
- 1. Go to [npmjs.com](https://www.npmjs.com/) and navigate to your package settings
25
- 2. Configure the trusted publisher (e.g., GitHub Actions)
26
- 3. Specify the repository and workflow that should be allowed to publish
27
- 4. Use the configured workflow to publish your actual package
28
-
29
- ## DO NOT USE THIS PACKAGE
30
-
31
- This package is a placeholder for OIDC configuration only. It:
32
- - Contains no executable code
33
- - Provides no functionality
34
- - Should not be installed as a dependency
35
- - Exists only for administrative purposes
36
-
37
- ## More Information
38
-
39
- For more details about npm's trusted publishing feature, see:
40
- - [npm Trusted Publishing Documentation](https://docs.npmjs.com/generating-provenance-statements)
41
- - [GitHub Actions OIDC Documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect)
42
-
43
- ---
44
-
45
- **Maintained for OIDC setup purposes only**