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.
@@ -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 path from "path";
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 = path.dirname(fileURLToPath(import.meta.url));
55
- const worker = [path.join(here, "catalog-worker.js"), path.join(here, "..", "catalog-worker.js")].find((p) => fs.existsSync(p));
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, path.join(root, "index.ts"), options, ts.sys).resolvedModule;
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 ?? path.relative(root, file), 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(path.join(root, "package.json"));
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, path.dirname(configPath));
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 = path.resolve(root, componentFile);
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 = path.relative(alias.dir, noExt);
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 = path.relative(path.dirname(path.resolve(root, canvasFile)), noExt);
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 path.join(alias.dir, spec.slice(alias.prefix.length));
290
- return path.resolve(root, spec);
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 = path.join(root, "tsconfig.json");
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, path.dirname(configPath));
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 ?? path.dirname(configPath);
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: path.resolve(base, targets[0].slice(0, -2)) };
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,