truecanvas 0.2.0 → 0.4.0
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/README.md +81 -181
- package/dist/catalog-worker.js +1 -1
- package/dist/chunk-M7B3W7EP.js +659 -0
- package/dist/{chunk-ZPARY6OD.js → chunk-OVYSWA2F.js} +387 -340
- package/dist/chunk-PBKX3SBC.js +97 -0
- package/dist/chunk-TSHD2S36.js +28 -0
- package/dist/{chunk-T2TCBSTG.js → chunk-WU7NBTJH.js} +25 -18
- package/dist/cli.js +989 -257
- package/dist/core/index.d.ts +56 -10
- package/dist/core/index.js +16 -25
- package/dist/editor/assets/{Menu-DXPm0EXo.css → Menu-BJ3OmqzH.css} +1 -1
- package/dist/editor/assets/hub-B5hqZCM2.js +2 -0
- package/dist/editor/assets/{main-DlADROMw.js → main-woTyWmya.js} +5 -5
- package/dist/editor/hub.html +3 -3
- package/dist/editor/index.html +3 -3
- package/dist/next/index.js +3 -2
- package/dist/share/index.d.ts +36 -0
- package/dist/share/index.js +6 -0
- package/dist/vite/index.d.ts +33 -0
- package/dist/vite/index.js +81 -0
- package/package.json +19 -6
- package/dist/editor/assets/hub-BnbEAnt6.js +0 -2
- /package/dist/editor/assets/{Menu-BFifVcgz.js → Menu-C0Ti-6T2.js} +0 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// src/core/config.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
function loadConfig(root, overrides = {}) {
|
|
5
|
+
let file = {};
|
|
6
|
+
const configPath = path.join(root, "truecanvas.config.json");
|
|
7
|
+
if (fs.existsSync(configPath)) file = JSON.parse(fs.readFileSync(configPath, "utf8"));
|
|
8
|
+
let deps = {};
|
|
9
|
+
try {
|
|
10
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(root, "package.json"), "utf8"));
|
|
11
|
+
deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
12
|
+
} catch {
|
|
13
|
+
deps = {};
|
|
14
|
+
}
|
|
15
|
+
const framework = file.framework ?? detectFramework(deps);
|
|
16
|
+
const vite = framework === "vite";
|
|
17
|
+
const appDir = file.appDir ?? (vite ? fs.existsSync(path.join(root, "src")) ? "src" : "." : fs.existsSync(path.join(root, "src/app")) ? "src/app" : "app");
|
|
18
|
+
const srcPrefix = appDir === "src" || appDir.startsWith("src/") ? "src/" : "";
|
|
19
|
+
const knownLibraries = ["@paper-design/shaders-react"].filter((l) => l in deps);
|
|
20
|
+
return {
|
|
21
|
+
root,
|
|
22
|
+
framework,
|
|
23
|
+
appUrl: vite ? "http://localhost:5173" : "http://localhost:3000",
|
|
24
|
+
port: 4800,
|
|
25
|
+
canvasDir: `${srcPrefix}canvas`,
|
|
26
|
+
appDir,
|
|
27
|
+
routeDir: vite ? ".truecanvas" : `${appDir}/truecanvas`,
|
|
28
|
+
components: [`${srcPrefix}components/**/*.tsx`],
|
|
29
|
+
darkMode: { strategy: "class", value: "dark" },
|
|
30
|
+
...file,
|
|
31
|
+
libraries: [.../* @__PURE__ */ new Set([...knownLibraries, ...file.libraries ?? []])],
|
|
32
|
+
...stripUndefined(overrides)
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function stripUndefined(o) {
|
|
36
|
+
return Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
|
|
37
|
+
}
|
|
38
|
+
function detectFramework(deps) {
|
|
39
|
+
return !("next" in deps) && "vite" in deps ? "vite" : "next";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// src/core/ast.ts
|
|
43
|
+
import { parse } from "@babel/parser";
|
|
44
|
+
function parseModule(source) {
|
|
45
|
+
return parse(source, {
|
|
46
|
+
sourceType: "module",
|
|
47
|
+
plugins: ["typescript", "jsx"],
|
|
48
|
+
errorRecovery: false
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
function idOf(node) {
|
|
52
|
+
const loc = node.loc.start;
|
|
53
|
+
return `${loc.line}:${loc.column}`;
|
|
54
|
+
}
|
|
55
|
+
function jsxName(name) {
|
|
56
|
+
if (name.type === "JSXIdentifier") return name.name;
|
|
57
|
+
if (name.type === "JSXNamespacedName") return `${name.namespace.name}:${name.name.name}`;
|
|
58
|
+
return `${jsxName(name.object)}.${name.property.name}`;
|
|
59
|
+
}
|
|
60
|
+
function walk(node, visit, parent = null) {
|
|
61
|
+
if (!node || typeof node !== "object") return;
|
|
62
|
+
if (Array.isArray(node)) {
|
|
63
|
+
for (const child of node) walk(child, visit, parent);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const n = node;
|
|
67
|
+
if (typeof n.type !== "string") return;
|
|
68
|
+
if (visit(n, parent) === false) return;
|
|
69
|
+
for (const key of Object.keys(n)) {
|
|
70
|
+
if (key === "loc" || key === "leadingComments" || key === "trailingComments" || key === "innerComments" || key === "extra") continue;
|
|
71
|
+
const value = n[key];
|
|
72
|
+
if (value && typeof value === "object") walk(value, visit, n);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
function isComponentName(name) {
|
|
76
|
+
return /^[A-Z]/.test(name) || name.includes(".");
|
|
77
|
+
}
|
|
78
|
+
function rewriteImportSource(statement, from, to) {
|
|
79
|
+
if (from === to) return statement;
|
|
80
|
+
for (const q of ['"', "'"]) {
|
|
81
|
+
const needle = `${q}${from}${q}`;
|
|
82
|
+
const i = statement.indexOf(needle);
|
|
83
|
+
if (i >= 0) return `${statement.slice(0, i)}${q}${to}${q}${statement.slice(i + needle.length)}`;
|
|
84
|
+
}
|
|
85
|
+
return statement;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export {
|
|
89
|
+
loadConfig,
|
|
90
|
+
detectFramework,
|
|
91
|
+
parseModule,
|
|
92
|
+
idOf,
|
|
93
|
+
jsxName,
|
|
94
|
+
walk,
|
|
95
|
+
isComponentName,
|
|
96
|
+
rewriteImportSource
|
|
97
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
idOf,
|
|
3
|
+
parseModule,
|
|
4
|
+
walk
|
|
5
|
+
} from "./chunk-PBKX3SBC.js";
|
|
6
|
+
|
|
7
|
+
// src/core/inject.ts
|
|
8
|
+
import MagicString from "magic-string";
|
|
9
|
+
function injectIds(source, file) {
|
|
10
|
+
let ast;
|
|
11
|
+
try {
|
|
12
|
+
ast = parseModule(source);
|
|
13
|
+
} catch {
|
|
14
|
+
return source;
|
|
15
|
+
}
|
|
16
|
+
const s = new MagicString(source);
|
|
17
|
+
walk(ast, (node) => {
|
|
18
|
+
if (node.type !== "JSXOpeningElement") return;
|
|
19
|
+
if (node.attributes.some((a) => a.type === "JSXAttribute" && a.name.name === "data-tc")) return;
|
|
20
|
+
const id = file ? `${file}#${idOf(node)}` : idOf(node);
|
|
21
|
+
s.appendLeft((node.typeParameters ?? node.name).end, ` data-tc="${id}"`);
|
|
22
|
+
});
|
|
23
|
+
return s.toString();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export {
|
|
27
|
+
injectIds
|
|
28
|
+
};
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
// src/core/catalog.ts
|
|
2
2
|
import fs from "fs";
|
|
3
|
-
import
|
|
3
|
+
import path2 from "path";
|
|
4
4
|
import { fileURLToPath } from "url";
|
|
5
5
|
import { createRequire } from "module";
|
|
6
6
|
import { globSync } from "tinyglobby";
|
|
7
|
+
|
|
8
|
+
// src/core/paths.ts
|
|
9
|
+
import path from "path";
|
|
10
|
+
var posix = (p) => p.split(path.sep).join("/");
|
|
11
|
+
|
|
12
|
+
// src/core/catalog.ts
|
|
7
13
|
var require2 = createRequire(import.meta.url);
|
|
8
14
|
var Catalog = class {
|
|
9
15
|
constructor(root, globs, libraries = []) {
|
|
@@ -51,8 +57,8 @@ var Catalog = class {
|
|
|
51
57
|
}
|
|
52
58
|
};
|
|
53
59
|
async function runAnalysis(root, globs, libraries) {
|
|
54
|
-
const here =
|
|
55
|
-
const worker = [
|
|
60
|
+
const here = path2.dirname(fileURLToPath(import.meta.url));
|
|
61
|
+
const worker = [path2.join(here, "catalog-worker.js"), path2.join(here, "..", "catalog-worker.js")].find((p) => fs.existsSync(p));
|
|
56
62
|
if (!worker) return analyzeComponents(root, globs, libraries);
|
|
57
63
|
const { Worker } = await import("worker_threads");
|
|
58
64
|
return new Promise((resolve, reject) => {
|
|
@@ -68,7 +74,7 @@ async function analyzeComponents(root, globs, libraries = []) {
|
|
|
68
74
|
const options = compilerOptions(ts, root);
|
|
69
75
|
const libEntries = /* @__PURE__ */ new Map();
|
|
70
76
|
for (const lib of libraries) {
|
|
71
|
-
const res = ts.resolveModuleName(lib,
|
|
77
|
+
const res = ts.resolveModuleName(lib, path2.join(root, "index.ts"), options, ts.sys).resolvedModule;
|
|
72
78
|
if (res) libEntries.set(res.resolvedFileName, lib);
|
|
73
79
|
}
|
|
74
80
|
const program = ts.createProgram({ rootNames: [...files, ...libEntries.keys()], options });
|
|
@@ -91,7 +97,7 @@ async function analyzeComponents(root, globs, libraries = []) {
|
|
|
91
97
|
if (!sig) continue;
|
|
92
98
|
const ret = checker.typeToString(sig.getReturnType());
|
|
93
99
|
if (!/Element|ReactNode|ReactPortal|null|any/.test(ret) || /^Promise</.test(ret)) continue;
|
|
94
|
-
out.push(describe(ts, checker, sym, decl, sig, library ??
|
|
100
|
+
out.push(describe(ts, checker, sym, decl, sig, library ?? posix(path2.relative(root, file)), library));
|
|
95
101
|
}
|
|
96
102
|
}
|
|
97
103
|
for (const spec of out) applyProfile(spec);
|
|
@@ -115,7 +121,7 @@ function applyProfile(spec) {
|
|
|
115
121
|
async function attachPresets(root, specs) {
|
|
116
122
|
const byLib = /* @__PURE__ */ new Map();
|
|
117
123
|
for (const s of specs) if (s.library) byLib.set(s.library, [...byLib.get(s.library) ?? [], s]);
|
|
118
|
-
const projectRequire = createRequire(
|
|
124
|
+
const projectRequire = createRequire(path2.join(root, "package.json"));
|
|
119
125
|
for (const [lib, list] of byLib) {
|
|
120
126
|
let mod;
|
|
121
127
|
try {
|
|
@@ -148,7 +154,7 @@ function compilerOptions(ts, root) {
|
|
|
148
154
|
const base = { jsx: ts.JsxEmit.ReactJSX, strict: true, skipLibCheck: true, noEmit: true, allowJs: false };
|
|
149
155
|
if (!configPath) return base;
|
|
150
156
|
const read = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
151
|
-
const parsed = ts.parseJsonConfigFileContent(read.config, ts.sys,
|
|
157
|
+
const parsed = ts.parseJsonConfigFileContent(read.config, ts.sys, path2.dirname(configPath));
|
|
152
158
|
return { ...parsed.options, noEmit: true, skipLibCheck: true, incremental: false, plugins: [] };
|
|
153
159
|
}
|
|
154
160
|
function describe(ts, checker, sym, decl, sig, file, library) {
|
|
@@ -273,25 +279,25 @@ function readDefaults(ts, decl) {
|
|
|
273
279
|
}
|
|
274
280
|
function importSpecifier(root, canvasFile, componentFile) {
|
|
275
281
|
if (!/\.(tsx|ts|jsx|js)$/.test(componentFile) && !componentFile.startsWith(".")) return componentFile;
|
|
276
|
-
const abs =
|
|
277
|
-
const noExt = abs.replace(/\.(tsx|ts|jsx|js)$/, "").replace(/\/index$/, "");
|
|
282
|
+
const abs = path2.resolve(root, componentFile);
|
|
283
|
+
const noExt = posix(abs).replace(/\.(tsx|ts|jsx|js)$/, "").replace(/\/index$/, "");
|
|
278
284
|
const alias = readAlias(root);
|
|
279
285
|
if (alias) {
|
|
280
|
-
const rel2 =
|
|
281
|
-
if (!rel2.startsWith("..")) return `${alias.prefix}${rel2}`;
|
|
286
|
+
const rel2 = path2.relative(alias.dir, noExt);
|
|
287
|
+
if (!rel2.startsWith("..")) return `${alias.prefix}${posix(rel2)}`;
|
|
282
288
|
}
|
|
283
|
-
let rel =
|
|
289
|
+
let rel = posix(path2.relative(path2.dirname(path2.resolve(root, canvasFile)), noExt));
|
|
284
290
|
if (!rel.startsWith(".")) rel = `./${rel}`;
|
|
285
291
|
return rel;
|
|
286
292
|
}
|
|
287
293
|
function resolveAliasPath(root, spec) {
|
|
288
294
|
const alias = readAlias(root);
|
|
289
|
-
if (alias && spec.startsWith(alias.prefix)) return
|
|
290
|
-
return
|
|
295
|
+
if (alias && spec.startsWith(alias.prefix)) return path2.join(alias.dir, spec.slice(alias.prefix.length));
|
|
296
|
+
return path2.resolve(root, spec);
|
|
291
297
|
}
|
|
292
298
|
var aliasCache = null;
|
|
293
299
|
function readAlias(root) {
|
|
294
|
-
const configFile =
|
|
300
|
+
const configFile = path2.join(root, "tsconfig.json");
|
|
295
301
|
let key = `${root}\0none`;
|
|
296
302
|
try {
|
|
297
303
|
key = `${root}\0${fs.statSync(configFile).mtimeMs}`;
|
|
@@ -304,12 +310,12 @@ function readAlias(root) {
|
|
|
304
310
|
const configPath = ts.findConfigFile(root, ts.sys.fileExists, "tsconfig.json");
|
|
305
311
|
if (configPath) {
|
|
306
312
|
const read = ts.readConfigFile(configPath, ts.sys.readFile);
|
|
307
|
-
const parsed = ts.parseJsonConfigFileContent(read.config, ts.sys,
|
|
313
|
+
const parsed = ts.parseJsonConfigFileContent(read.config, ts.sys, path2.dirname(configPath));
|
|
308
314
|
const paths = parsed.options.paths ?? {};
|
|
309
|
-
const base = parsed.options.pathsBasePath ?? parsed.options.baseUrl ??
|
|
315
|
+
const base = parsed.options.pathsBasePath ?? parsed.options.baseUrl ?? path2.dirname(configPath);
|
|
310
316
|
for (const [k, targets] of Object.entries(paths)) {
|
|
311
317
|
if (k.endsWith("/*") && targets[0]?.endsWith("/*")) {
|
|
312
|
-
value = { prefix: k.slice(0, -1), dir:
|
|
318
|
+
value = { prefix: k.slice(0, -1), dir: path2.resolve(base, targets[0].slice(0, -2)) };
|
|
313
319
|
break;
|
|
314
320
|
}
|
|
315
321
|
}
|
|
@@ -322,6 +328,7 @@ function readAlias(root) {
|
|
|
322
328
|
}
|
|
323
329
|
|
|
324
330
|
export {
|
|
331
|
+
posix,
|
|
325
332
|
Catalog,
|
|
326
333
|
analyzeComponents,
|
|
327
334
|
importSpecifier,
|