@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.
- package/dist/cache.d.ts +18 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +71 -0
- package/dist/cache.js.map +1 -0
- package/dist/generator.d.ts +6 -0
- package/dist/generator.d.ts.map +1 -0
- package/dist/generator.js +10 -0
- package/dist/generator.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +152 -0
- package/dist/index.js.map +1 -0
- package/dist/sidecar.d.ts +17 -0
- package/dist/sidecar.d.ts.map +1 -0
- package/dist/sidecar.js +68 -0
- package/dist/sidecar.js.map +1 -0
- package/dist/types.d.ts +28 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +43 -7
- package/src/cache.ts +75 -0
- package/src/generator.ts +11 -0
- package/src/index.ts +172 -0
- package/src/sidecar.ts +78 -0
- package/src/types.ts +30 -0
- package/README.md +0 -45
package/dist/cache.d.ts
ADDED
|
@@ -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 @@
|
|
|
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"}
|
package/dist/index.d.ts
ADDED
|
@@ -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"}
|
package/dist/sidecar.js
ADDED
|
@@ -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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"}
|
package/dist/types.d.ts
ADDED
|
@@ -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 @@
|
|
|
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.
|
|
4
|
-
"description": "
|
|
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
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
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
|
+
}
|
package/src/generator.ts
ADDED
|
@@ -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**
|