@open-slide/core 1.0.4 → 1.0.6
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/{build-DqfKmw9h.js → build-4wOJF1l4.js} +1 -1
- package/dist/cli/bin.js +3 -3
- package/dist/{config-DweCbRkQ.d.ts → config-D2y1AXaN.d.ts} +3 -0
- package/dist/{config-CN7J0RDO.js → config-evLWCV1-.js} +378 -222
- package/dist/{dev-jWxtWHAG.js → dev-BUr0S-Ij.js} +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/locale/index.d.ts +24 -0
- package/dist/locale/index.js +1189 -0
- package/dist/{preview-CSA05Gfm.js → preview-DP_gIphz.js} +1 -1
- package/dist/types-BVvl_xup.d.ts +314 -0
- package/dist/vite/index.d.ts +2 -1
- package/dist/vite/index.js +1 -1
- package/package.json +7 -1
- package/src/app/app.tsx +6 -2
- package/src/app/components/asset-view.tsx +87 -64
- package/src/app/components/click-nav-zones.tsx +4 -2
- package/src/app/components/inspector/comment-widget.tsx +9 -7
- package/src/app/components/inspector/inspect-overlay.tsx +79 -17
- package/src/app/components/inspector/inspector-panel.tsx +68 -39
- package/src/app/components/inspector/inspector-provider.tsx +185 -58
- package/src/app/components/inspector/save-bar.tsx +6 -5
- package/src/app/components/panel/save-card.tsx +12 -9
- package/src/app/components/pdf-progress-toast.tsx +11 -4
- package/src/app/components/player.tsx +7 -25
- package/src/app/components/present/control-bar.tsx +17 -10
- package/src/app/components/present/help-overlay.tsx +18 -17
- package/src/app/components/present/overview-grid.tsx +6 -9
- package/src/app/components/present/use-presenter-channel.ts +3 -10
- package/src/app/components/sidebar/folder-item.tsx +16 -9
- package/src/app/components/sidebar/icon-picker.tsx +4 -5
- package/src/app/components/sidebar/sidebar.tsx +87 -25
- package/src/app/components/slide-canvas.tsx +1 -10
- package/src/app/components/style-panel/design-provider.tsx +2 -6
- package/src/app/components/style-panel/style-panel.tsx +26 -18
- package/src/app/components/theme-toggle.tsx +7 -5
- package/src/app/components/thumbnail-rail.tsx +4 -2
- package/src/app/favicon.ico +0 -0
- package/src/app/lib/export-html.ts +1 -9
- package/src/app/lib/export-pdf.ts +0 -5
- package/src/app/lib/inspector/use-editor.ts +9 -7
- package/src/app/lib/print-ready.ts +0 -4
- package/src/app/lib/sdk.ts +1 -2
- package/src/app/lib/use-locale.ts +20 -0
- package/src/app/routes/home.tsx +90 -45
- package/src/app/routes/presenter.tsx +45 -25
- package/src/app/routes/slide.tsx +37 -24
- package/src/app/styles.css +28 -0
- package/src/app/virtual.d.ts +4 -0
- package/src/locale/en.ts +303 -0
- package/src/locale/format.ts +12 -0
- package/src/locale/index.ts +6 -0
- package/src/locale/ja.ts +307 -0
- package/src/locale/types.ts +323 -0
- package/src/locale/zh-cn.ts +303 -0
- package/src/locale/zh-tw.ts +303 -0
|
@@ -7,6 +7,9 @@ import { existsSync } from "node:fs";
|
|
|
7
7
|
import tailwindcss from "@tailwindcss/vite";
|
|
8
8
|
import react from "@vitejs/plugin-react";
|
|
9
9
|
import { parse } from "@babel/parser";
|
|
10
|
+
import * as t$1 from "@babel/types";
|
|
11
|
+
import * as t from "@babel/types";
|
|
12
|
+
import { isJSXElement, isJSXFragment } from "@babel/types";
|
|
10
13
|
import fg from "fast-glob";
|
|
11
14
|
import { loadConfigFromFile } from "vite";
|
|
12
15
|
|
|
@@ -21,28 +24,34 @@ const SKIP_KEYS = new Set([
|
|
|
21
24
|
"trailingComments",
|
|
22
25
|
"innerComments"
|
|
23
26
|
]);
|
|
24
|
-
function
|
|
27
|
+
function walk(ast, visit, accept) {
|
|
25
28
|
let stopped = false;
|
|
26
|
-
const
|
|
29
|
+
const recurse = (node) => {
|
|
27
30
|
if (stopped || !node || typeof node !== "object") return;
|
|
28
31
|
if (Array.isArray(node)) {
|
|
29
|
-
for (const c of node)
|
|
32
|
+
for (const c of node) recurse(c);
|
|
30
33
|
return;
|
|
31
34
|
}
|
|
32
35
|
const n = node;
|
|
33
36
|
if (typeof n.type !== "string") return;
|
|
34
|
-
if (n
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
return;
|
|
38
|
-
}
|
|
37
|
+
if (accept(n) && visit(n) === "stop") {
|
|
38
|
+
stopped = true;
|
|
39
|
+
return;
|
|
39
40
|
}
|
|
40
41
|
for (const key of Object.keys(n)) {
|
|
41
42
|
if (SKIP_KEYS.has(key)) continue;
|
|
42
|
-
|
|
43
|
+
recurse(n[key]);
|
|
43
44
|
}
|
|
44
45
|
};
|
|
45
|
-
|
|
46
|
+
recurse(ast);
|
|
47
|
+
}
|
|
48
|
+
const isJsx = (n) => isJSXElement(n) || isJSXFragment(n);
|
|
49
|
+
const acceptAll = () => true;
|
|
50
|
+
function walkJsx(ast, visit) {
|
|
51
|
+
walk(ast, visit, isJsx);
|
|
52
|
+
}
|
|
53
|
+
function walkAll(ast, visit) {
|
|
54
|
+
walk(ast, visit, acceptAll);
|
|
46
55
|
}
|
|
47
56
|
|
|
48
57
|
//#endregion
|
|
@@ -126,54 +135,44 @@ function lineIndent(source, lineNumber) {
|
|
|
126
135
|
function findJsxAncestors(ast, line, column) {
|
|
127
136
|
const hits = [];
|
|
128
137
|
walkJsx(ast, (n) => {
|
|
129
|
-
if (!n.loc) return;
|
|
138
|
+
if (!n.loc || !t$1.isJSXElement(n) && !t$1.isJSXFragment(n)) return;
|
|
130
139
|
const s = n.loc.start;
|
|
131
140
|
const e = n.loc.end;
|
|
132
141
|
const afterStart = line > s.line || line === s.line && column >= s.column;
|
|
133
142
|
const beforeEnd = line < e.line || line === e.line && column < e.column;
|
|
134
143
|
if (afterStart && beforeEnd) hits.push({
|
|
135
144
|
node: n,
|
|
136
|
-
size: n.end - n.start
|
|
145
|
+
size: (n.end ?? 0) - (n.start ?? 0)
|
|
137
146
|
});
|
|
138
147
|
});
|
|
139
148
|
hits.sort((a, b) => a.size - b.size);
|
|
140
149
|
return hits.map((h) => h.node);
|
|
141
150
|
}
|
|
142
151
|
function planInsertion(source, target) {
|
|
143
|
-
if (target
|
|
152
|
+
if (t$1.isJSXFragment(target)) {
|
|
144
153
|
const opening = target.openingFragment;
|
|
145
|
-
if (!opening) return null;
|
|
146
154
|
const startLine = target.loc?.start.line ?? 1;
|
|
147
155
|
return {
|
|
148
|
-
offset: opening.end,
|
|
156
|
+
offset: opening.end ?? 0,
|
|
149
157
|
indent: `${lineIndent(source, startLine)} `
|
|
150
158
|
};
|
|
151
159
|
}
|
|
152
|
-
if (target
|
|
160
|
+
if (t$1.isJSXElement(target)) {
|
|
153
161
|
const opening = target.openingElement;
|
|
154
|
-
if (
|
|
162
|
+
if (opening.selfClosing) return null;
|
|
155
163
|
const startLine = target.loc?.start.line ?? 1;
|
|
156
164
|
return {
|
|
157
|
-
offset: opening.end,
|
|
165
|
+
offset: opening.end ?? 0,
|
|
158
166
|
indent: `${lineIndent(source, startLine)} `
|
|
159
167
|
};
|
|
160
168
|
}
|
|
161
169
|
return null;
|
|
162
170
|
}
|
|
163
171
|
function findInsertion(source, line, column) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
ast = parse(source, {
|
|
167
|
-
sourceType: "module",
|
|
168
|
-
plugins: ["typescript", "jsx"],
|
|
169
|
-
errorRecovery: true
|
|
170
|
-
});
|
|
171
|
-
} catch {
|
|
172
|
-
return null;
|
|
173
|
-
}
|
|
172
|
+
const ast = parseSource$1(source);
|
|
173
|
+
if (!ast) return null;
|
|
174
174
|
const col = column ?? 0;
|
|
175
175
|
const ancestors = findJsxAncestors(ast, line, col);
|
|
176
|
-
if (ancestors.length === 0) return null;
|
|
177
176
|
for (const node of ancestors) {
|
|
178
177
|
const plan = planInsertion(source, node);
|
|
179
178
|
if (plan) return plan;
|
|
@@ -196,19 +195,16 @@ function parseSource$1(source) {
|
|
|
196
195
|
return null;
|
|
197
196
|
}
|
|
198
197
|
}
|
|
199
|
-
function findInnermostJsxElement(
|
|
200
|
-
const ast = parseSource$1(source);
|
|
201
|
-
if (!ast) return null;
|
|
198
|
+
function findInnermostJsxElement(ast, line, column) {
|
|
202
199
|
const exact = findJsxByStart(ast, line, column);
|
|
203
200
|
if (exact) return exact;
|
|
204
|
-
const
|
|
205
|
-
for (const n of ancestors) if (n.type === "JSXElement") return n;
|
|
201
|
+
for (const n of findJsxAncestors(ast, line, column)) if (t$1.isJSXElement(n)) return n;
|
|
206
202
|
return null;
|
|
207
203
|
}
|
|
208
204
|
function findJsxByStart(ast, line, column) {
|
|
209
205
|
let hit = null;
|
|
210
206
|
walkJsx(ast, (n) => {
|
|
211
|
-
if (n
|
|
207
|
+
if (!t$1.isJSXElement(n) || !n.loc) return;
|
|
212
208
|
const s = n.loc.start;
|
|
213
209
|
if (s.line === line && s.column === column) {
|
|
214
210
|
hit = n;
|
|
@@ -220,60 +216,56 @@ function findJsxByStart(ast, line, column) {
|
|
|
220
216
|
function jsString$1(s) {
|
|
221
217
|
return `'${s.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/\n/g, "\\n")}'`;
|
|
222
218
|
}
|
|
223
|
-
function
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
if (name?.type === "JSXIdentifier" && name.name === "style") return attr;
|
|
229
|
-
}
|
|
219
|
+
function jsxAttrName(attr) {
|
|
220
|
+
return t$1.isJSXIdentifier(attr.name) ? attr.name.name : null;
|
|
221
|
+
}
|
|
222
|
+
function findJsxAttr(opening, name) {
|
|
223
|
+
for (const attr of opening.attributes) if (t$1.isJSXAttribute(attr) && jsxAttrName(attr) === name) return attr;
|
|
230
224
|
return null;
|
|
231
225
|
}
|
|
232
226
|
function buildStyleSplice(source, element, ops) {
|
|
233
227
|
const opening = element.openingElement;
|
|
234
|
-
|
|
235
|
-
const existing = findStyleAttr(opening);
|
|
228
|
+
const existing = findJsxAttr(opening, "style");
|
|
236
229
|
const style = new Map();
|
|
237
230
|
if (existing) {
|
|
238
231
|
const value = existing.value;
|
|
239
|
-
if (!value || value
|
|
232
|
+
if (!value || !t$1.isJSXExpressionContainer(value)) return { error: "style attribute has unsupported form" };
|
|
240
233
|
const expr = value.expression;
|
|
241
|
-
if (expr
|
|
242
|
-
const
|
|
243
|
-
|
|
244
|
-
if (prop.
|
|
245
|
-
const p = prop;
|
|
246
|
-
if (p.computed) return { error: "style has computed key" };
|
|
234
|
+
if (!t$1.isObjectExpression(expr)) return { error: "style is not a literal object" };
|
|
235
|
+
for (const prop of expr.properties) {
|
|
236
|
+
if (!t$1.isObjectProperty(prop)) return { error: "style contains spread or method" };
|
|
237
|
+
if (prop.computed) return { error: "style has computed key" };
|
|
247
238
|
let keyName = null;
|
|
248
|
-
if (
|
|
249
|
-
else if (
|
|
239
|
+
if (t$1.isIdentifier(prop.key)) keyName = prop.key.name;
|
|
240
|
+
else if (t$1.isStringLiteral(prop.key)) keyName = prop.key.value;
|
|
250
241
|
if (!keyName) return { error: "style has unsupported key" };
|
|
251
|
-
|
|
242
|
+
const v = prop.value;
|
|
243
|
+
if (typeof v.start !== "number" || typeof v.end !== "number") return { error: "style value missing source range" };
|
|
244
|
+
style.set(keyName, source.slice(v.start, v.end));
|
|
252
245
|
}
|
|
253
246
|
}
|
|
254
247
|
for (const op of ops) if (op.value === null) style.delete(op.key);
|
|
255
248
|
else style.set(op.key, jsString$1(op.value));
|
|
256
249
|
if (style.size === 0) {
|
|
257
250
|
if (!existing) return null;
|
|
258
|
-
let from = existing.start;
|
|
251
|
+
let from = existing.start ?? 0;
|
|
259
252
|
if (from > 0 && source[from - 1] === " ") from -= 1;
|
|
260
253
|
return {
|
|
261
254
|
from,
|
|
262
|
-
to: existing.end,
|
|
255
|
+
to: existing.end ?? 0,
|
|
263
256
|
text: ""
|
|
264
257
|
};
|
|
265
258
|
}
|
|
266
259
|
const propsText = Array.from(style.entries()).map(([k, v]) => `${k}: ${v}`).join(", ");
|
|
267
260
|
const newAttr = `style={{ ${propsText} }}`;
|
|
268
261
|
if (existing) return {
|
|
269
|
-
from: existing.start,
|
|
270
|
-
to: existing.end,
|
|
262
|
+
from: existing.start ?? 0,
|
|
263
|
+
to: existing.end ?? 0,
|
|
271
264
|
text: newAttr
|
|
272
265
|
};
|
|
273
|
-
const name = opening.name;
|
|
274
266
|
return {
|
|
275
|
-
from: name.end,
|
|
276
|
-
to: name.end,
|
|
267
|
+
from: opening.name.end ?? 0,
|
|
268
|
+
to: opening.name.end ?? 0,
|
|
277
269
|
text: ` ${newAttr}`
|
|
278
270
|
};
|
|
279
271
|
}
|
|
@@ -281,57 +273,280 @@ function formatJsxText(value) {
|
|
|
281
273
|
if (/[{}<>]/.test(value) || /^\s|\s$/.test(value) || value === "") return `{${jsString$1(value)}}`;
|
|
282
274
|
return value;
|
|
283
275
|
}
|
|
284
|
-
function
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const meaningful = children.filter((c) => {
|
|
288
|
-
if (c.type === "JSXText") {
|
|
289
|
-
const v = c.value;
|
|
290
|
-
return v.trim() !== "";
|
|
291
|
-
}
|
|
276
|
+
function meaningfulChildren(parent) {
|
|
277
|
+
return parent.children.filter((c) => {
|
|
278
|
+
if (t$1.isJSXText(c)) return c.value.trim() !== "";
|
|
292
279
|
return true;
|
|
293
280
|
});
|
|
294
|
-
|
|
281
|
+
}
|
|
282
|
+
function wrapSplice(parent, text) {
|
|
283
|
+
const first = parent.children[0];
|
|
284
|
+
const last = parent.children[parent.children.length - 1];
|
|
285
|
+
return {
|
|
286
|
+
from: first.start ?? 0,
|
|
287
|
+
to: last.end ?? 0,
|
|
288
|
+
text
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
function collectTextCandidates(element, out) {
|
|
292
|
+
const meaningful = meaningfulChildren(element);
|
|
293
|
+
const isSole = meaningful.length === 1;
|
|
294
|
+
for (const child of meaningful) if (t$1.isJSXText(child)) {
|
|
295
|
+
const current = child.value.trim();
|
|
296
|
+
if (!current) continue;
|
|
297
|
+
out.push({
|
|
298
|
+
current,
|
|
299
|
+
splice: (v) => isSole ? wrapSplice(element, formatJsxText(v)) : {
|
|
300
|
+
from: child.start ?? 0,
|
|
301
|
+
to: child.end ?? 0,
|
|
302
|
+
text: formatJsxText(v)
|
|
303
|
+
}
|
|
304
|
+
});
|
|
305
|
+
} else if (t$1.isJSXExpressionContainer(child)) {
|
|
306
|
+
const expr = child.expression;
|
|
307
|
+
if (t$1.isStringLiteral(expr) || t$1.isNumericLiteral(expr)) {
|
|
308
|
+
const current = String(expr.value);
|
|
309
|
+
out.push({
|
|
310
|
+
current,
|
|
311
|
+
splice: (v) => isSole ? wrapSplice(element, `{${jsString$1(v)}}`) : {
|
|
312
|
+
from: child.start ?? 0,
|
|
313
|
+
to: child.end ?? 0,
|
|
314
|
+
text: `{${jsString$1(v)}}`
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
} else if (t$1.isJSXElement(child) || t$1.isJSXFragment(child)) collectTextCandidates(child, out);
|
|
319
|
+
}
|
|
320
|
+
function propPassthroughName(element) {
|
|
321
|
+
const meaningful = meaningfulChildren(element);
|
|
322
|
+
if (meaningful.length !== 1) return null;
|
|
295
323
|
const child = meaningful[0];
|
|
296
|
-
if (child
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
324
|
+
if (!t$1.isJSXExpressionContainer(child)) return null;
|
|
325
|
+
return t$1.isIdentifier(child.expression) ? child.expression.name : null;
|
|
326
|
+
}
|
|
327
|
+
function findEnclosingComponent(ast, target) {
|
|
328
|
+
let best = null;
|
|
329
|
+
let bestSize = Number.POSITIVE_INFINITY;
|
|
330
|
+
const targetStart = target.start ?? 0;
|
|
331
|
+
const targetEnd = target.end ?? 0;
|
|
332
|
+
const consider = (name, fn) => {
|
|
333
|
+
if (!/^[A-Z]/.test(name)) return;
|
|
334
|
+
const fnStart = fn.start ?? 0;
|
|
335
|
+
const fnEnd = fn.end ?? 0;
|
|
336
|
+
if (fnStart > targetStart || fnEnd < targetEnd) return;
|
|
337
|
+
const size = fnEnd - fnStart;
|
|
338
|
+
if (size < bestSize) {
|
|
339
|
+
best = {
|
|
340
|
+
name,
|
|
341
|
+
fn
|
|
342
|
+
};
|
|
343
|
+
bestSize = size;
|
|
344
|
+
}
|
|
345
|
+
};
|
|
346
|
+
const visitDecl = (decl) => {
|
|
347
|
+
if (t$1.isFunctionDeclaration(decl) && decl.id) consider(decl.id.name, decl);
|
|
348
|
+
else if (t$1.isVariableDeclaration(decl)) for (const d of decl.declarations) {
|
|
349
|
+
if (!t$1.isVariableDeclarator(d) || !t$1.isIdentifier(d.id) || !d.init) continue;
|
|
350
|
+
if (t$1.isArrowFunctionExpression(d.init) || t$1.isFunctionExpression(d.init)) consider(d.id.name, d.init);
|
|
351
|
+
}
|
|
352
|
+
};
|
|
353
|
+
for (const decl of ast.program.body) {
|
|
354
|
+
visitDecl(decl);
|
|
355
|
+
if (t$1.isExportNamedDeclaration(decl) || t$1.isExportDefaultDeclaration(decl)) {
|
|
356
|
+
const inner = decl.declaration;
|
|
357
|
+
if (inner && (t$1.isStatement(inner) || t$1.isFunctionDeclaration(inner))) visitDecl(inner);
|
|
358
|
+
}
|
|
304
359
|
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
360
|
+
return best;
|
|
361
|
+
}
|
|
362
|
+
function componentDestructuresProp(fn, propName) {
|
|
363
|
+
if (fn.params.length === 0) return false;
|
|
364
|
+
let first = fn.params[0];
|
|
365
|
+
if (t$1.isAssignmentPattern(first)) first = first.left;
|
|
366
|
+
if (!t$1.isObjectPattern(first)) return false;
|
|
367
|
+
for (const prop of first.properties) {
|
|
368
|
+
if (!t$1.isObjectProperty(prop)) continue;
|
|
369
|
+
if (t$1.isIdentifier(prop.key) && prop.key.name === propName) return true;
|
|
370
|
+
if (t$1.isStringLiteral(prop.key) && prop.key.value === propName) return true;
|
|
371
|
+
}
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
function collectCallSiteCandidates(ast, componentName) {
|
|
375
|
+
const out = [];
|
|
376
|
+
walkJsx(ast, (n) => {
|
|
377
|
+
if (!t$1.isJSXElement(n)) return;
|
|
378
|
+
const elName = n.openingElement.name;
|
|
379
|
+
if (t$1.isJSXIdentifier(elName) && elName.name === componentName) collectTextCandidates(n, out);
|
|
380
|
+
});
|
|
381
|
+
return out;
|
|
382
|
+
}
|
|
383
|
+
function formatJsxAttrValue(value) {
|
|
384
|
+
if (/^[^"\\<>&{}\n\r]*$/.test(value)) return `"${value}"`;
|
|
385
|
+
return `{${jsString$1(value)}}`;
|
|
386
|
+
}
|
|
387
|
+
function spliceRange(node, text) {
|
|
388
|
+
return {
|
|
389
|
+
from: node.start ?? 0,
|
|
390
|
+
to: node.end ?? 0,
|
|
391
|
+
text
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
function collectPropCallSiteCandidates(ast, componentName, propName) {
|
|
395
|
+
const out = [];
|
|
396
|
+
walkJsx(ast, (n) => {
|
|
397
|
+
if (!t$1.isJSXElement(n)) return;
|
|
398
|
+
const elName = n.openingElement.name;
|
|
399
|
+
if (!t$1.isJSXIdentifier(elName) || elName.name !== componentName) return;
|
|
400
|
+
const attr = findJsxAttr(n.openingElement, propName);
|
|
401
|
+
if (!attr?.value) return;
|
|
402
|
+
const v = attr.value;
|
|
403
|
+
if (t$1.isStringLiteral(v)) out.push({
|
|
404
|
+
current: v.value,
|
|
405
|
+
splice: (s) => spliceRange(v, formatJsxAttrValue(s))
|
|
406
|
+
});
|
|
407
|
+
else if (t$1.isJSXExpressionContainer(v)) {
|
|
408
|
+
const expr = v.expression;
|
|
409
|
+
if (t$1.isStringLiteral(expr) || t$1.isNumericLiteral(expr)) out.push({
|
|
410
|
+
current: String(expr.value),
|
|
411
|
+
splice: (s) => spliceRange(v, formatJsxAttrValue(s))
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
return out;
|
|
416
|
+
}
|
|
417
|
+
function findEnclosingMapCallback(ast, target) {
|
|
418
|
+
let best = null;
|
|
419
|
+
const targetStart = target.start ?? 0;
|
|
420
|
+
const targetEnd = target.end ?? 0;
|
|
421
|
+
walkAll(ast, (node) => {
|
|
422
|
+
if (!t$1.isCallExpression(node)) return;
|
|
423
|
+
const callee = node.callee;
|
|
424
|
+
if (!t$1.isMemberExpression(callee) || callee.computed) return;
|
|
425
|
+
if (!t$1.isIdentifier(callee.property)) return;
|
|
426
|
+
if (callee.property.name !== "map" && callee.property.name !== "flatMap") return;
|
|
427
|
+
const fn = node.arguments[0];
|
|
428
|
+
if (!fn || !t$1.isArrowFunctionExpression(fn) && !t$1.isFunctionExpression(fn)) return;
|
|
429
|
+
const fnStart = fn.start ?? 0;
|
|
430
|
+
const fnEnd = fn.end ?? 0;
|
|
431
|
+
if (fnStart > targetStart || fnEnd < targetEnd) return;
|
|
432
|
+
if (!t$1.isExpression(callee.object)) return;
|
|
433
|
+
const size = fnEnd - fnStart;
|
|
434
|
+
if (!best || size < best.size) best = {
|
|
435
|
+
fn,
|
|
436
|
+
arrayArg: callee.object,
|
|
437
|
+
size
|
|
311
438
|
};
|
|
312
|
-
|
|
439
|
+
});
|
|
440
|
+
if (!best) return null;
|
|
441
|
+
const found = best;
|
|
442
|
+
return {
|
|
443
|
+
fn: found.fn,
|
|
444
|
+
arrayArg: found.arrayArg
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
function resolveArrayLiteralElements(ast, expr) {
|
|
448
|
+
const dropHoles = (arr) => arr.elements.filter((e) => e != null);
|
|
449
|
+
if (t$1.isArrayExpression(expr)) return dropHoles(expr);
|
|
450
|
+
if (!t$1.isIdentifier(expr)) return null;
|
|
451
|
+
const name = expr.name;
|
|
452
|
+
const useStart = expr.start ?? 0;
|
|
453
|
+
let init = null;
|
|
454
|
+
walkAll(ast, (node) => {
|
|
455
|
+
if (!t$1.isVariableDeclarator(node)) return;
|
|
456
|
+
if (!t$1.isIdentifier(node.id) || node.id.name !== name) return;
|
|
457
|
+
if (!node.init || !t$1.isArrayExpression(node.init)) return;
|
|
458
|
+
if ((node.init.start ?? 0) > useStart) return;
|
|
459
|
+
init = node.init;
|
|
460
|
+
});
|
|
461
|
+
return init ? dropHoles(init) : null;
|
|
462
|
+
}
|
|
463
|
+
function findObjectProperty(obj, name) {
|
|
464
|
+
if (!t$1.isObjectExpression(obj)) return null;
|
|
465
|
+
for (const prop of obj.properties) {
|
|
466
|
+
if (!t$1.isObjectProperty(prop) || prop.computed) continue;
|
|
467
|
+
if (t$1.isIdentifier(prop.key) && prop.key.name === name) return prop;
|
|
468
|
+
if (t$1.isStringLiteral(prop.key) && prop.key.value === name) return prop;
|
|
469
|
+
}
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
function decodeMapPassthrough(element, callbackParam) {
|
|
473
|
+
const meaningful = meaningfulChildren(element);
|
|
474
|
+
if (meaningful.length !== 1) return null;
|
|
475
|
+
const child = meaningful[0];
|
|
476
|
+
if (!t$1.isJSXExpressionContainer(child)) return null;
|
|
477
|
+
const expr = child.expression;
|
|
478
|
+
if (t$1.isMemberExpression(expr)) {
|
|
479
|
+
if (expr.computed) return null;
|
|
480
|
+
if (!t$1.isIdentifier(expr.object) || !t$1.isIdentifier(expr.property)) return null;
|
|
481
|
+
if (!callbackParam || !t$1.isIdentifier(callbackParam)) return null;
|
|
482
|
+
if (callbackParam.name !== expr.object.name) return null;
|
|
483
|
+
return expr.property.name;
|
|
484
|
+
}
|
|
485
|
+
if (t$1.isIdentifier(expr)) {
|
|
486
|
+
const fieldName = expr.name;
|
|
487
|
+
if (!callbackParam || !t$1.isObjectPattern(callbackParam)) return null;
|
|
488
|
+
for (const prop of callbackParam.properties) {
|
|
489
|
+
if (!t$1.isObjectProperty(prop) || prop.computed) continue;
|
|
490
|
+
if (!t$1.isIdentifier(prop.key) || prop.key.name !== fieldName) continue;
|
|
491
|
+
return t$1.isIdentifier(prop.value) && prop.value.name === fieldName ? fieldName : null;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
function collectArrayMapCandidates(ast, element) {
|
|
497
|
+
const ctx = findEnclosingMapCallback(ast, element);
|
|
498
|
+
if (!ctx) return [];
|
|
499
|
+
const fieldName = decodeMapPassthrough(element, ctx.fn.params[0]);
|
|
500
|
+
if (!fieldName) return [];
|
|
501
|
+
const elements = resolveArrayLiteralElements(ast, ctx.arrayArg);
|
|
502
|
+
if (!elements) return [];
|
|
503
|
+
const out = [];
|
|
504
|
+
for (const obj of elements) {
|
|
505
|
+
const prop = findObjectProperty(obj, fieldName);
|
|
506
|
+
if (!prop) continue;
|
|
507
|
+
const v = prop.value;
|
|
508
|
+
if (t$1.isStringLiteral(v)) out.push({
|
|
509
|
+
current: v.value,
|
|
510
|
+
splice: (s) => spliceRange(v, jsString$1(s))
|
|
511
|
+
});
|
|
512
|
+
else if (t$1.isNumericLiteral(v)) out.push({
|
|
513
|
+
current: String(v.value),
|
|
514
|
+
splice: (s) => spliceRange(v, jsString$1(s))
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
return out;
|
|
518
|
+
}
|
|
519
|
+
function buildTextSplice(ast, element, value, prevText) {
|
|
520
|
+
const candidates = [];
|
|
521
|
+
collectTextCandidates(element, candidates);
|
|
522
|
+
if (candidates.length === 0) {
|
|
523
|
+
const passthrough = propPassthroughName(element);
|
|
524
|
+
const enclosing = passthrough ? findEnclosingComponent(ast, element) : null;
|
|
525
|
+
if (passthrough === "children" && enclosing) candidates.push(...collectCallSiteCandidates(ast, enclosing.name));
|
|
526
|
+
else if (passthrough && enclosing && componentDestructuresProp(enclosing.fn, passthrough)) candidates.push(...collectPropCallSiteCandidates(ast, enclosing.name, passthrough));
|
|
313
527
|
}
|
|
314
|
-
|
|
528
|
+
if (candidates.length === 0) candidates.push(...collectArrayMapCandidates(ast, element));
|
|
529
|
+
if (candidates.length === 0) return { error: "element has no editable text" };
|
|
530
|
+
if (candidates.length === 1) return candidates[0].splice(value);
|
|
531
|
+
if (prevText === void 0) return { error: "element has multiple text candidates; missing prevText" };
|
|
532
|
+
const norm = prevText.trim();
|
|
533
|
+
const matches = candidates.filter((c) => c.current === norm);
|
|
534
|
+
if (matches.length === 0) return { error: "no text candidate matches the current value" };
|
|
535
|
+
if (matches.length > 1) return { error: "multiple text candidates share the same value; cannot disambiguate" };
|
|
536
|
+
return matches[0].splice(value);
|
|
315
537
|
}
|
|
316
538
|
function findImports$1(ast) {
|
|
317
|
-
const body = ast.program?.body ?? [];
|
|
318
539
|
const out = [];
|
|
319
|
-
for (const node of body) {
|
|
320
|
-
if (node
|
|
321
|
-
const src = node.source?.value;
|
|
322
|
-
if (typeof src !== "string") continue;
|
|
323
|
-
const specs = node.specifiers ?? [];
|
|
540
|
+
for (const node of ast.program.body) {
|
|
541
|
+
if (!t$1.isImportDeclaration(node)) continue;
|
|
324
542
|
let def = null;
|
|
325
|
-
for (const spec of
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
def = local;
|
|
329
|
-
break;
|
|
330
|
-
}
|
|
543
|
+
for (const spec of node.specifiers) if (t$1.isImportDefaultSpecifier(spec)) {
|
|
544
|
+
def = spec.local.name;
|
|
545
|
+
break;
|
|
331
546
|
}
|
|
332
547
|
out.push({
|
|
333
548
|
node,
|
|
334
|
-
source:
|
|
549
|
+
source: node.source.value,
|
|
335
550
|
defaultIdent: def
|
|
336
551
|
});
|
|
337
552
|
}
|
|
@@ -341,11 +556,7 @@ function collectTopLevelIdentifiers(ast) {
|
|
|
341
556
|
const names = new Set();
|
|
342
557
|
for (const imp of findImports$1(ast)) {
|
|
343
558
|
if (imp.defaultIdent) names.add(imp.defaultIdent);
|
|
344
|
-
const
|
|
345
|
-
for (const spec of specs) if (spec.type !== "ImportDefaultSpecifier") {
|
|
346
|
-
const local = spec.local?.name;
|
|
347
|
-
if (typeof local === "string") names.add(local);
|
|
348
|
-
}
|
|
559
|
+
for (const spec of imp.node.specifiers) if (!t$1.isImportDefaultSpecifier(spec)) names.add(spec.local.name);
|
|
349
560
|
}
|
|
350
561
|
return names;
|
|
351
562
|
}
|
|
@@ -368,56 +579,43 @@ function safeAssetIdentifier(filename, taken) {
|
|
|
368
579
|
}
|
|
369
580
|
return candidate;
|
|
370
581
|
}
|
|
371
|
-
function
|
|
372
|
-
const attrs = opening.attributes ?? [];
|
|
373
|
-
for (const attr of attrs) {
|
|
374
|
-
if (attr.type !== "JSXAttribute") continue;
|
|
375
|
-
const n = attr.name;
|
|
376
|
-
if (n?.type === "JSXIdentifier" && n.name === name) return attr;
|
|
377
|
-
}
|
|
378
|
-
return null;
|
|
379
|
-
}
|
|
380
|
-
function planAssetAttr(ast, element, attr, assetPath) {
|
|
381
|
-
const opening = element.openingElement;
|
|
382
|
-
if (!opening) return { error: "no opening element" };
|
|
383
|
-
if (!attr || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(attr)) return { error: "invalid attribute name" };
|
|
384
|
-
if (!assetPath.startsWith("./assets/")) return { error: "asset path must start with ./assets/" };
|
|
582
|
+
function planAssetImport(ast, assetPath) {
|
|
385
583
|
const imports = findImports$1(ast);
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
importSplice = {
|
|
584
|
+
for (const imp of imports) if (imp.source === assetPath && imp.defaultIdent) return {
|
|
585
|
+
identifier: imp.defaultIdent,
|
|
586
|
+
importSplice: null
|
|
587
|
+
};
|
|
588
|
+
const filename = assetPath.slice(assetPath.lastIndexOf("/") + 1);
|
|
589
|
+
const identifier = safeAssetIdentifier(filename, collectTopLevelIdentifiers(ast));
|
|
590
|
+
const importStmt = `import ${identifier} from '${assetPath.replace(/'/g, "\\'")}';\n`;
|
|
591
|
+
const last = imports[imports.length - 1];
|
|
592
|
+
const insertAt = last ? last.node.end ?? 0 : 0;
|
|
593
|
+
const prefix = last ? "\n" : "";
|
|
594
|
+
return {
|
|
595
|
+
identifier,
|
|
596
|
+
importSplice: {
|
|
400
597
|
from: insertAt,
|
|
401
598
|
to: insertAt,
|
|
402
599
|
text: prefix + importStmt
|
|
403
|
-
}
|
|
404
|
-
}
|
|
600
|
+
}
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
function planAssetAttr(ast, element, attr, assetPath) {
|
|
604
|
+
if (!attr || !/^[A-Za-z_][A-Za-z0-9_]*$/.test(attr)) return { error: "invalid attribute name" };
|
|
605
|
+
if (!assetPath.startsWith("./assets/")) return { error: "asset path must start with ./assets/" };
|
|
606
|
+
const { identifier, importSplice } = planAssetImport(ast, assetPath);
|
|
607
|
+
const opening = element.openingElement;
|
|
405
608
|
const newAttr = `${attr}={${identifier}}`;
|
|
406
609
|
const existing = findJsxAttr(opening, attr);
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
to: existing.end,
|
|
610
|
+
const attrSplice = existing ? {
|
|
611
|
+
from: existing.start ?? 0,
|
|
612
|
+
to: existing.end ?? 0,
|
|
411
613
|
text: newAttr
|
|
614
|
+
} : {
|
|
615
|
+
from: opening.name.end ?? 0,
|
|
616
|
+
to: opening.name.end ?? 0,
|
|
617
|
+
text: ` ${newAttr}`
|
|
412
618
|
};
|
|
413
|
-
else {
|
|
414
|
-
const name = opening.name;
|
|
415
|
-
attrSplice = {
|
|
416
|
-
from: name.end,
|
|
417
|
-
to: name.end,
|
|
418
|
-
text: ` ${newAttr}`
|
|
419
|
-
};
|
|
420
|
-
}
|
|
421
619
|
return {
|
|
422
620
|
importSplice,
|
|
423
621
|
attrSplice
|
|
@@ -425,71 +623,36 @@ function planAssetAttr(ast, element, attr, assetPath) {
|
|
|
425
623
|
}
|
|
426
624
|
function readJsxStringAttr(opening, name) {
|
|
427
625
|
const attr = findJsxAttr(opening, name);
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
if (
|
|
431
|
-
if (
|
|
432
|
-
if (value.type === "JSXExpressionContainer") {
|
|
433
|
-
const expr = value.expression;
|
|
434
|
-
if (expr.type === "StringLiteral") return expr.value;
|
|
435
|
-
}
|
|
626
|
+
const v = attr?.value;
|
|
627
|
+
if (!v) return null;
|
|
628
|
+
if (t$1.isStringLiteral(v)) return v.value;
|
|
629
|
+
if (t$1.isJSXExpressionContainer(v) && t$1.isStringLiteral(v.expression)) return v.expression.value;
|
|
436
630
|
return null;
|
|
437
631
|
}
|
|
438
632
|
function readJsxNumberAttr(opening, name) {
|
|
439
633
|
const attr = findJsxAttr(opening, name);
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
if (!
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
const n = expr.value;
|
|
446
|
-
return Number.isFinite(n) ? n : null;
|
|
447
|
-
}
|
|
448
|
-
return null;
|
|
634
|
+
const v = attr?.value;
|
|
635
|
+
if (!v || !t$1.isJSXExpressionContainer(v)) return null;
|
|
636
|
+
if (!t$1.isNumericLiteral(v.expression)) return null;
|
|
637
|
+
const n = v.expression.value;
|
|
638
|
+
return Number.isFinite(n) ? n : null;
|
|
449
639
|
}
|
|
450
640
|
function planReplacePlaceholder(ast, element, assetPath) {
|
|
451
641
|
const opening = element.openingElement;
|
|
452
|
-
if (!opening) return { error: "
|
|
453
|
-
const elName = opening.name;
|
|
454
|
-
if (elName?.type !== "JSXIdentifier" || elName.name !== "ImagePlaceholder") return { error: "not a placeholder" };
|
|
642
|
+
if (!t$1.isJSXIdentifier(opening.name) || opening.name.name !== "ImagePlaceholder") return { error: "not a placeholder" };
|
|
455
643
|
if (!assetPath.startsWith("./assets/")) return { error: "asset path must start with ./assets/" };
|
|
456
644
|
const hint = readJsxStringAttr(opening, "hint") ?? "";
|
|
457
645
|
const width = readJsxNumberAttr(opening, "width");
|
|
458
646
|
const height = readJsxNumberAttr(opening, "height");
|
|
459
|
-
const
|
|
460
|
-
let identifier = null;
|
|
461
|
-
for (const imp of imports) if (imp.source === assetPath && imp.defaultIdent) {
|
|
462
|
-
identifier = imp.defaultIdent;
|
|
463
|
-
break;
|
|
464
|
-
}
|
|
465
|
-
let importSplice = null;
|
|
466
|
-
if (!identifier) {
|
|
467
|
-
const filename = assetPath.slice(assetPath.lastIndexOf("/") + 1);
|
|
468
|
-
const taken = collectTopLevelIdentifiers(ast);
|
|
469
|
-
identifier = safeAssetIdentifier(filename, taken);
|
|
470
|
-
const importStmt = `import ${identifier} from '${assetPath.replace(/'/g, "\\'")}';\n`;
|
|
471
|
-
const insertAt = imports.length > 0 ? imports[imports.length - 1].node.end : 0;
|
|
472
|
-
const prefix = imports.length > 0 ? "\n" : "";
|
|
473
|
-
importSplice = {
|
|
474
|
-
from: insertAt,
|
|
475
|
-
to: insertAt,
|
|
476
|
-
text: prefix + importStmt
|
|
477
|
-
};
|
|
478
|
-
}
|
|
647
|
+
const { identifier, importSplice } = planAssetImport(ast, assetPath);
|
|
479
648
|
const styleParts = [];
|
|
480
649
|
if (width != null) styleParts.push(`width: ${width}`);
|
|
481
650
|
if (height != null) styleParts.push(`height: ${height}`);
|
|
482
651
|
styleParts.push(`objectFit: 'cover'`);
|
|
483
|
-
const
|
|
484
|
-
const altAttr = ` alt=${jsString$1(hint)}`;
|
|
485
|
-
const replacement = `<img src={${identifier}}${altAttr}${styleAttr} />`;
|
|
652
|
+
const replacement = `<img src={${identifier}} alt=${jsString$1(hint)} style={{ ${styleParts.join(", ")} }} />`;
|
|
486
653
|
return {
|
|
487
654
|
importSplice,
|
|
488
|
-
elementSplice:
|
|
489
|
-
from: element.start,
|
|
490
|
-
to: element.end,
|
|
491
|
-
text: replacement
|
|
492
|
-
}
|
|
655
|
+
elementSplice: spliceRange(element, replacement)
|
|
493
656
|
};
|
|
494
657
|
}
|
|
495
658
|
function applyEdit(source, line, column, ops) {
|
|
@@ -497,7 +660,13 @@ function applyEdit(source, line, column, ops) {
|
|
|
497
660
|
ok: true,
|
|
498
661
|
source
|
|
499
662
|
};
|
|
500
|
-
const
|
|
663
|
+
const ast = parseSource$1(source);
|
|
664
|
+
if (!ast) return {
|
|
665
|
+
ok: false,
|
|
666
|
+
status: 422,
|
|
667
|
+
error: "could not parse source"
|
|
668
|
+
};
|
|
669
|
+
const element = findInnermostJsxElement(ast, line, column);
|
|
501
670
|
if (!element) return {
|
|
502
671
|
ok: false,
|
|
503
672
|
status: 422,
|
|
@@ -519,7 +688,7 @@ function applyEdit(source, line, column, ops) {
|
|
|
519
688
|
}
|
|
520
689
|
for (const op of ops) {
|
|
521
690
|
if (op.kind !== "set-text") continue;
|
|
522
|
-
const result = buildTextSplice(element, op.value);
|
|
691
|
+
const result = buildTextSplice(ast, element, op.value, op.prevText);
|
|
523
692
|
if ("error" in result) return {
|
|
524
693
|
ok: false,
|
|
525
694
|
status: 422,
|
|
@@ -530,12 +699,6 @@ function applyEdit(source, line, column, ops) {
|
|
|
530
699
|
const assetOps = ops.flatMap((op) => op.kind === "set-attr-asset" ? [op] : []);
|
|
531
700
|
const placeholderOps = ops.flatMap((op) => op.kind === "replace-placeholder-with-image" ? [op] : []);
|
|
532
701
|
if (assetOps.length > 0 || placeholderOps.length > 0) {
|
|
533
|
-
const ast = parseSource$1(source);
|
|
534
|
-
if (!ast) return {
|
|
535
|
-
ok: false,
|
|
536
|
-
status: 422,
|
|
537
|
-
error: "could not parse source"
|
|
538
|
-
};
|
|
539
702
|
const importSplices = [];
|
|
540
703
|
for (const op of assetOps) {
|
|
541
704
|
const plan = planAssetAttr(ast, element, op.attr, op.assetPath);
|
|
@@ -1703,19 +1866,11 @@ function filesPlugin(opts) {
|
|
|
1703
1866
|
//#region src/vite/loc-tags-plugin.ts
|
|
1704
1867
|
const FORWARDING_COMPONENTS = new Set(["ImagePlaceholder"]);
|
|
1705
1868
|
function isTaggableJsxName(name) {
|
|
1706
|
-
if (!name
|
|
1707
|
-
|
|
1708
|
-
if (n.type !== "JSXIdentifier" || typeof n.name !== "string") return false;
|
|
1709
|
-
return /^[a-z]/.test(n.name) || FORWARDING_COMPONENTS.has(n.name);
|
|
1869
|
+
if (!t.isJSXIdentifier(name)) return false;
|
|
1870
|
+
return /^[a-z]/.test(name.name) || FORWARDING_COMPONENTS.has(name.name);
|
|
1710
1871
|
}
|
|
1711
1872
|
function alreadyTagged(opening) {
|
|
1712
|
-
|
|
1713
|
-
for (const attr of attrs) {
|
|
1714
|
-
if (attr.type !== "JSXAttribute") continue;
|
|
1715
|
-
const name = attr.name;
|
|
1716
|
-
if (name?.type === "JSXIdentifier" && name.name === "data-slide-loc") return true;
|
|
1717
|
-
}
|
|
1718
|
-
return false;
|
|
1873
|
+
return opening.attributes.some((attr) => t.isJSXAttribute(attr) && t.isJSXIdentifier(attr.name) && attr.name.name === "data-slide-loc");
|
|
1719
1874
|
}
|
|
1720
1875
|
function injectLocTags(code) {
|
|
1721
1876
|
let ast;
|
|
@@ -1730,17 +1885,13 @@ function injectLocTags(code) {
|
|
|
1730
1885
|
}
|
|
1731
1886
|
const insertions = [];
|
|
1732
1887
|
walkJsx(ast, (node) => {
|
|
1733
|
-
if (node
|
|
1888
|
+
if (!t.isJSXElement(node) || !node.loc) return;
|
|
1734
1889
|
const opening = node.openingElement;
|
|
1735
|
-
if (!opening) return;
|
|
1736
1890
|
const name = opening.name;
|
|
1737
|
-
if (!isTaggableJsxName(name)) return;
|
|
1738
|
-
if (alreadyTagged(opening)) return;
|
|
1739
|
-
const loc = node.loc;
|
|
1740
|
-
if (!loc) return;
|
|
1891
|
+
if (!isTaggableJsxName(name) || alreadyTagged(opening)) return;
|
|
1741
1892
|
insertions.push({
|
|
1742
|
-
offset: name.end,
|
|
1743
|
-
text: ` data-slide-loc="${loc.start.line}:${loc.start.column}"`
|
|
1893
|
+
offset: name.end ?? 0,
|
|
1894
|
+
text: ` data-slide-loc="${node.loc.start.line}:${node.loc.start.column}"`
|
|
1744
1895
|
});
|
|
1745
1896
|
});
|
|
1746
1897
|
if (insertions.length === 0) return null;
|
|
@@ -1811,7 +1962,7 @@ function toId(absFile, slidesRoot) {
|
|
|
1811
1962
|
function generateSlidesModule(files, slidesRoot, isDev) {
|
|
1812
1963
|
const entries = files.map((abs) => {
|
|
1813
1964
|
const id = toId(abs, slidesRoot);
|
|
1814
|
-
const importPath = isDev ? `/@fs
|
|
1965
|
+
const importPath = isDev ? `/@fs/${abs.replace(/^\/+/, "")}` : abs;
|
|
1815
1966
|
return {
|
|
1816
1967
|
id,
|
|
1817
1968
|
importPath
|
|
@@ -1953,6 +2104,7 @@ async function createViteConfig(opts) {
|
|
|
1953
2104
|
return {
|
|
1954
2105
|
root: APP_ROOT,
|
|
1955
2106
|
configFile: false,
|
|
2107
|
+
envDir: userCwd,
|
|
1956
2108
|
plugins: [
|
|
1957
2109
|
locTagsPlugin({
|
|
1958
2110
|
userCwd,
|
|
@@ -1978,6 +2130,10 @@ async function createViteConfig(opts) {
|
|
|
1978
2130
|
optimizeDeps: {
|
|
1979
2131
|
entries: [path.join(APP_ROOT, "main.tsx")],
|
|
1980
2132
|
include: [
|
|
2133
|
+
"react",
|
|
2134
|
+
"react-dom",
|
|
2135
|
+
"react-dom/client",
|
|
2136
|
+
"next-themes",
|
|
1981
2137
|
"react-router-dom",
|
|
1982
2138
|
"radix-ui",
|
|
1983
2139
|
"lucide-react",
|