@tishlang/vite-plugin-tish 2.39.0 → 2.43.8

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.
Files changed (2) hide show
  1. package/index.js +88 -19
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -4,6 +4,9 @@
4
4
  //
5
5
  // Dev compiles request a source map (`tish compile-module --source-map`) so Vite's error overlay
6
6
  // and the browser debugger resolve back to the original `.tish`.
7
+ //
8
+ // Platform/surface cascade (RN-style `Button.macos.tish` / `.web.tish` / …) is owned by the `tish`
9
+ // CLI (`tish resolve-id`); this plugin must not reimplement those rules.
7
10
 
8
11
  import { execFileSync } from "node:child_process";
9
12
  import { existsSync } from "node:fs";
@@ -17,14 +20,70 @@ const TISH_EXT = ".tish";
17
20
  * @param {string} [opts.projectRoot] Root for resolving bare specifiers / node_modules (default: Vite root).
18
21
  * @param {"hmr"|"full-reload"} [opts.mode] `hmr` (default) hot-swaps modules; `full-reload` reloads the
19
22
  * page on any `.tish` change (the documented fallback for `--target bytecode` apps).
23
+ * @param {string} [opts.platform] Platform token for resolve cascade (`macos`, `ios`, `web`, …).
24
+ * @param {string} [opts.surface] Surface token (`native`, `webview`, `web`). Defaults from
25
+ * `TISH_PLATFORM` / `TISH_SURFACE` when unset.
20
26
  */
21
27
  export default function tishPlugin(opts = {}) {
22
28
  const tishPath = opts.tishPath ?? process.env.TISH_PATH ?? "tish";
23
29
  const mode = opts.mode ?? "hmr";
30
+ const platform = opts.platform ?? process.env.TISH_PLATFORM;
31
+ const surface = opts.surface ?? process.env.TISH_SURFACE;
24
32
  let projectRoot = opts.projectRoot;
33
+ /** @type {boolean|null} */
34
+ let supportsPlatformFlags = null;
35
+
36
+ function envForChild() {
37
+ const env = { ...process.env };
38
+ if (platform) env.TISH_PLATFORM = platform;
39
+ if (surface) env.TISH_SURFACE = surface;
40
+ return env;
41
+ }
42
+
43
+ function detectPlatformFlags() {
44
+ if (supportsPlatformFlags !== null) return supportsPlatformFlags;
45
+ try {
46
+ const help = execFileSync(tishPath, ["compile-module", "--help"], {
47
+ encoding: "utf8",
48
+ env: envForChild(),
49
+ });
50
+ supportsPlatformFlags =
51
+ help.includes("--platform") && help.includes("--surface");
52
+ } catch {
53
+ supportsPlatformFlags = false;
54
+ }
55
+ return supportsPlatformFlags;
56
+ }
57
+
58
+ function platformArgs() {
59
+ if (!detectPlatformFlags()) return [];
60
+ const args = [];
61
+ if (platform) args.push("--platform", platform);
62
+ if (surface) args.push("--surface", surface);
63
+ return args;
64
+ }
65
+
66
+ function runTish(args, maxBuffer = 1024 * 1024) {
67
+ return execFileSync(tishPath, args, {
68
+ encoding: "utf8",
69
+ maxBuffer,
70
+ env: envForChild(),
71
+ });
72
+ }
73
+
74
+ /** Resolve relative imports through `tish resolve-id` (same cascade as compile). */
75
+ function resolveTishId(source, importer) {
76
+ if (!detectPlatformFlags()) return null;
77
+ const args = ["resolve-id", source, ...platformArgs()];
78
+ if (importer) args.push("--importer", importer.split("?")[0]);
79
+ try {
80
+ const out = runTish(args).trim();
81
+ return out || null;
82
+ } catch {
83
+ return null;
84
+ }
85
+ }
25
86
 
26
- // Compile a single `.tish` file to one ES module. In `hmr` mode we request a source map and parse
27
- // the `{ js, map }` envelope; otherwise we take raw JS on stdout.
28
87
  function compileModule(file) {
29
88
  const args = [
30
89
  "compile-module",
@@ -34,18 +93,29 @@ export default function tishPlugin(opts = {}) {
34
93
  "--format",
35
94
  "esm",
36
95
  "--vite-dev",
96
+ ...platformArgs(),
37
97
  ];
38
98
  args.push(mode === "hmr" ? "--source-map" : "--no-source-map");
39
99
  if (projectRoot) args.push("--project-root", projectRoot);
40
- const stdout = execFileSync(tishPath, args, {
41
- encoding: "utf8",
42
- maxBuffer: 64 * 1024 * 1024,
43
- });
44
- if (mode === "hmr") {
45
- const { js, map } = JSON.parse(stdout);
46
- return { code: js, map };
100
+ try {
101
+ const stdout = runTish(args, 64 * 1024 * 1024);
102
+ if (mode === "hmr") {
103
+ const { js, map } = JSON.parse(stdout);
104
+ return { code: js, map };
105
+ }
106
+ return { code: stdout, map: null };
107
+ } catch (err) {
108
+ const msg = String(err?.stderr || err?.message || err);
109
+ // Older tish CLIs reject --platform; retry once without flags (env still set).
110
+ if (
111
+ supportsPlatformFlags !== false &&
112
+ /unexpected argument '--platform'|unexpected argument '--surface'/.test(msg)
113
+ ) {
114
+ supportsPlatformFlags = false;
115
+ return compileModule(file);
116
+ }
117
+ throw err;
47
118
  }
48
- return { code: stdout, map: null };
49
119
  }
50
120
 
51
121
  return {
@@ -56,15 +126,17 @@ export default function tishPlugin(opts = {}) {
56
126
  if (!projectRoot) projectRoot = config.root;
57
127
  },
58
128
 
59
- // Bring `.tish` imports into Vite's module graph: resolve relative `.tish` specifiers to absolute
60
- // file paths so Vite addresses each module and can invalidate it individually. Root-relative URLs
61
- // (`/src/x.tish`) and bare specifiers are left to Vite's resolver, which maps them to real files.
62
129
  resolveId(source, importer) {
63
- if (!source.endsWith(TISH_EXT)) return null;
130
+ // Relative imports: always try platform cascade (with or without `.tish`).
64
131
  if (source.startsWith(".") && importer) {
65
- return path.resolve(path.dirname(importer.split("?")[0]), source);
132
+ const resolved = resolveTishId(source, importer);
133
+ if (resolved) return resolved;
134
+ if (source.endsWith(TISH_EXT)) {
135
+ return path.resolve(path.dirname(importer.split("?")[0]), source);
136
+ }
137
+ return null;
66
138
  }
67
- if (path.isAbsolute(source) && existsSync(source)) {
139
+ if (source.endsWith(TISH_EXT) && path.isAbsolute(source) && existsSync(source)) {
68
140
  return source;
69
141
  }
70
142
  return null;
@@ -75,8 +147,6 @@ export default function tishPlugin(opts = {}) {
75
147
  if (!file.endsWith(TISH_EXT)) return null;
76
148
  const { code, map } = compileModule(file);
77
149
  if (mode !== "hmr") return { code, map };
78
- // Self-accepting boundary: editing this module re-runs it without reloading the page. Frameworks
79
- // (e.g. Lattish) can register their own `import.meta.hot.accept` handlers on top.
80
150
  const withBoundary = `${code}\nif (import.meta.hot) { import.meta.hot.accept(); }\n`;
81
151
  return { code: withBoundary, map };
82
152
  },
@@ -88,7 +158,6 @@ export default function tishPlugin(opts = {}) {
88
158
  server.ws.send({ type: "full-reload" });
89
159
  return [];
90
160
  }
91
- // Hand Vite the changed module node(s) so it performs per-module HMR rather than a full reload.
92
161
  const mods = server.moduleGraph.getModulesByFile(file);
93
162
  return mods ? [...mods] : undefined;
94
163
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tishlang/vite-plugin-tish",
3
- "version": "2.39.0",
3
+ "version": "2.43.8",
4
4
  "description": "Vite plugin for Tish: in-graph .tish compilation with HMR and source maps for dev.",
5
5
  "license": "PIF",
6
6
  "type": "module",