@colixsystems/widget-sdk 0.116.0 → 0.118.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 +48 -1
- package/dist/contract.cjs +30 -3
- package/dist/contract.js +30 -3
- package/dist/devserver.js +92 -40
- package/dist/flatten-entry.js +817 -0
- package/dist/host.d.ts +8 -4
- package/dist/linter.js +3 -146
- package/dist/source-mask.js +162 -0
- package/dist/theme-components.cjs +70 -10
- package/dist/theme-components.js +70 -10
- package/package.json +7 -3
package/README.md
CHANGED
|
@@ -70,7 +70,54 @@ See the design reference for the full architecture: [`docs/architecture/widget-m
|
|
|
70
70
|
|
|
71
71
|
## Status
|
|
72
72
|
|
|
73
|
-
`v0.
|
|
73
|
+
`v0.118.0` — pre-publish. The package surface (types, function names, export paths) is the v1 contract; runtime behaviour for some hooks is stubbed (each hook documents what's wired and what isn't). It is **not yet published to npm**.
|
|
74
|
+
|
|
75
|
+
### What's new in 0.118.0 (contract 1.90.0)
|
|
76
|
+
|
|
77
|
+
**A `styleSchema` field's `default` now actually applies.** Declaring `default` on a style field wrote it into the manifest and nothing ever read it back, so a widget's own styling baseline — and any design saved from the Widget Builder preview — was silently dropped on the next render. The host now resolves it onto `props.style`.
|
|
78
|
+
|
|
79
|
+
It is the **weakest** layer, deliberately: it applies only when nothing above it sets that field.
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
styleSchema `default` -> palette / components.<scope> -> widgetStyles[manifestId] -> per-instance props.style
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
So a workspace theme still outranks a widget's own baseline, and a field you leave undefaulted keeps following the theme exactly as before. Nothing changes for a widget that declares no style defaults.
|
|
86
|
+
|
|
87
|
+
The host still does **not** apply style to elements — your widget owns placement and keeps reading `props.style.<field>` (or `useWidgetStyle()`) and applying each value where it chooses:
|
|
88
|
+
|
|
89
|
+
```jsx
|
|
90
|
+
const style = useWidgetStyle();
|
|
91
|
+
<View style={[styles.card, style.cardBackground && { backgroundColor: style.cardBackground }]}>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Keep `themeDefault` for a fallback that IS a theme token: it stays display-only (a greyed placeholder in the Style panel) so the field tracks the workspace theme. Use `default` for a literal constant your code genuinely falls back to — that value now reaches `props.style`, so it must match what your code applies.
|
|
95
|
+
|
|
96
|
+
### What's new in 0.117.0 (contract 1.89.0)
|
|
97
|
+
|
|
98
|
+
**`expo-sensors` is a vetted import — widgets can read device motion.** The allowlist held no sensor package, so a step counter, a shake control, a tilt/level, or a compass had nothing to read the hardware with. `expo-sensors` (Accelerometer, Gyroscope, Magnetometer, DeviceMotion, Barometer, Pedometer, LightSensor) is now on the list as **native-only** (`platforms: ["native"]`, category `sensors`) and pinned by the Expo export.
|
|
99
|
+
|
|
100
|
+
Native-only is deliberate rather than a gap: the package's own web build derives "acceleration" from `deviceorientation` **angles**, not real motion, so a shake threshold tuned in the Player would behave differently in the exported app. Split the widget and read the browser API directly on web:
|
|
101
|
+
|
|
102
|
+
```jsx
|
|
103
|
+
// widget.native.jsx
|
|
104
|
+
import { Accelerometer } from "expo-sensors";
|
|
105
|
+
|
|
106
|
+
Accelerometer.setUpdateInterval(100);
|
|
107
|
+
const sub = Accelerometer.addListener(({ x, y, z }) => setReading({ x, y, z }));
|
|
108
|
+
return () => sub.remove(); // ALWAYS remove it — a live sensor drains the battery
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```jsx
|
|
112
|
+
// widget.web.jsx — window.DeviceMotionEvent exposes the same hardware
|
|
113
|
+
const onMotion = (e) => setReading(e.accelerationIncludingGravity);
|
|
114
|
+
window.addEventListener("devicemotion", onMotion);
|
|
115
|
+
return () => window.removeEventListener("devicemotion", onMotion);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Start the reading from a **user gesture** on both hosts — iOS Safari also needs an explicit `DeviceMotionEvent.requestPermission()` grant, and neither host delivers readings to a listener attached on mount.
|
|
119
|
+
|
|
120
|
+
**Fixed: a `expo-haptics` widget failed the native build.** `expo-haptics` has been vetted since the package expansion but was never pinned in the exported app's `package.json`, so a widget importing it rendered in the web Player and broke the Expo bundle with "Unable to resolve module expo-haptics". It is pinned now, and the pairing is no longer hand-maintained: every native-capable entry on the vetted list is checked against the export's dependency set by a contract-derived test.
|
|
74
121
|
|
|
75
122
|
### What's new in 0.116.0 (contract 1.88.0)
|
|
76
123
|
|
package/dist/contract.cjs
CHANGED
|
@@ -1967,8 +1967,11 @@ const MANIFEST_SCHEMA = {
|
|
|
1967
1967
|
"exposes; the Studio Properties Panel renders a \"Style\" section from " +
|
|
1968
1968
|
"it. The author's resolved values are delivered to the widget under " +
|
|
1969
1969
|
"props.style (one object keyed by style-field name); the widget reads " +
|
|
1970
|
-
"props.style.<field> and applies each wherever it chooses.
|
|
1971
|
-
"
|
|
1970
|
+
"props.style.<field> and applies each wherever it chooses. A field's " +
|
|
1971
|
+
"own `default` is resolved onto props.style as the WEAKEST layer " +
|
|
1972
|
+
"(sc-6750): it applies only when no theme layer and no per-instance " +
|
|
1973
|
+
"value sets that field. The host never applies style to elements " +
|
|
1974
|
+
"— the widget owns placement.",
|
|
1972
1975
|
default: {},
|
|
1973
1976
|
},
|
|
1974
1977
|
rendersOwnChrome: {
|
|
@@ -2571,6 +2574,13 @@ const VETTED_IMPORTS = [
|
|
|
2571
2574
|
description:
|
|
2572
2575
|
"Renders a QR code, drawn as SVG through the vetted react-native-svg — so one `<QRCode value={…} size={…} />` covers both platforms with no split file. Pure JS with no native module of its own: the web host resolves it and the Expo export pins it, exactly like date-fns. Its `text-encoding` polyfill is opt-in for React Native < 0.75 only; the export ships RN 0.85.3, which has a global TextEncoder, so the Metro transformer that package documents is deliberately not wired up.",
|
|
2573
2576
|
},
|
|
2577
|
+
{
|
|
2578
|
+
specifier: "expo-sensors",
|
|
2579
|
+
platforms: ["native"],
|
|
2580
|
+
category: "sensors",
|
|
2581
|
+
description:
|
|
2582
|
+
"Device motion hardware on the Expo export: Accelerometer, Gyroscope, Magnetometer, DeviceMotion, Barometer, Pedometer and LightSensor, each read as an addListener subscription with setUpdateInterval — always remove the subscription on unmount, a sensor left running drains the battery. Expo SDK 56 ships 56.0.x. Native-only on purpose: the package's own web build derives acceleration from deviceorientation ANGLES rather than real motion, so a shake or tilt threshold tuned on one host would read differently on the other. Author it in widget.native.jsx and pair it with a widget.web.jsx reading window.DeviceMotionEvent (accelerationIncludingGravity / rotationRate), the browser API the same hardware exposes. Both hosts need a user gesture before readings start, and iOS Safari additionally needs an explicit DeviceMotionEvent.requestPermission() grant — so gate the reading behind a Pressable, never start it on mount.",
|
|
2583
|
+
},
|
|
2574
2584
|
];
|
|
2575
2585
|
|
|
2576
2586
|
// sc-1064: CORE React infrastructure specifiers the host RESOLVES at runtime
|
|
@@ -3500,7 +3510,24 @@ const CONTRACT = deepFreeze({
|
|
|
3500
3510
|
// widget can open at SCREEN level. Everything before it was clipped by the
|
|
3501
3511
|
// layout container the widget sits in, so a preview or dialog could not leave
|
|
3502
3512
|
// the widget's own tile on either host.
|
|
3503
|
-
|
|
3513
|
+
// 1.89.0: additive — `expo-sensors` joins VETTED_IMPORTS as a native-only
|
|
3514
|
+
// `sensors` package (Accelerometer / Gyroscope / DeviceMotion / Barometer /
|
|
3515
|
+
// Pedometer / LightSensor). Widgets had no path to device motion at all: the
|
|
3516
|
+
// allowlist held no sensor package, so a step counter, a tilt/shake control,
|
|
3517
|
+
// a level, or a compass was unbuildable. Native-only because the package's
|
|
3518
|
+
// web build reads deviceorientation angles rather than real acceleration —
|
|
3519
|
+
// the web half is a widget.web.jsx over window.DeviceMotionEvent. Pinned in
|
|
3520
|
+
// the compiler's export dependencies, like every other native-module member.
|
|
3521
|
+
// 1.90.0: additive (sc-6750) — a `styleSchema` field's declared `default`
|
|
3522
|
+
// now RESOLVES onto `props.style`, as the weakest style layer. It was
|
|
3523
|
+
// written into the manifest and then read by nothing, so an author's saved
|
|
3524
|
+
// colour was silently dropped on the next render. Precedence is unchanged
|
|
3525
|
+
// above it: styleSchema `default` -> palette/`components.<scope>` ->
|
|
3526
|
+
// `widgetStyles[manifestId]` -> per-instance `props.style`, so a workspace
|
|
3527
|
+
// theme still outranks a widget's own baseline and a field the author never
|
|
3528
|
+
// defaulted follows the theme exactly as before. The host still never
|
|
3529
|
+
// applies style to elements — the widget owns placement.
|
|
3530
|
+
version: "1.90.0",
|
|
3504
3531
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
3505
3532
|
hooks: HOOKS,
|
|
3506
3533
|
primitives: PRIMITIVES,
|
package/dist/contract.js
CHANGED
|
@@ -1967,8 +1967,11 @@ const MANIFEST_SCHEMA = {
|
|
|
1967
1967
|
"exposes; the Studio Properties Panel renders a \"Style\" section from " +
|
|
1968
1968
|
"it. The author's resolved values are delivered to the widget under " +
|
|
1969
1969
|
"props.style (one object keyed by style-field name); the widget reads " +
|
|
1970
|
-
"props.style.<field> and applies each wherever it chooses.
|
|
1971
|
-
"
|
|
1970
|
+
"props.style.<field> and applies each wherever it chooses. A field's " +
|
|
1971
|
+
"own `default` is resolved onto props.style as the WEAKEST layer " +
|
|
1972
|
+
"(sc-6750): it applies only when no theme layer and no per-instance " +
|
|
1973
|
+
"value sets that field. The host never applies style to elements " +
|
|
1974
|
+
"— the widget owns placement.",
|
|
1972
1975
|
default: {},
|
|
1973
1976
|
},
|
|
1974
1977
|
rendersOwnChrome: {
|
|
@@ -2571,6 +2574,13 @@ const VETTED_IMPORTS = [
|
|
|
2571
2574
|
description:
|
|
2572
2575
|
"Renders a QR code, drawn as SVG through the vetted react-native-svg — so one `<QRCode value={…} size={…} />` covers both platforms with no split file. Pure JS with no native module of its own: the web host resolves it and the Expo export pins it, exactly like date-fns. Its `text-encoding` polyfill is opt-in for React Native < 0.75 only; the export ships RN 0.85.3, which has a global TextEncoder, so the Metro transformer that package documents is deliberately not wired up.",
|
|
2573
2576
|
},
|
|
2577
|
+
{
|
|
2578
|
+
specifier: "expo-sensors",
|
|
2579
|
+
platforms: ["native"],
|
|
2580
|
+
category: "sensors",
|
|
2581
|
+
description:
|
|
2582
|
+
"Device motion hardware on the Expo export: Accelerometer, Gyroscope, Magnetometer, DeviceMotion, Barometer, Pedometer and LightSensor, each read as an addListener subscription with setUpdateInterval — always remove the subscription on unmount, a sensor left running drains the battery. Expo SDK 56 ships 56.0.x. Native-only on purpose: the package's own web build derives acceleration from deviceorientation ANGLES rather than real motion, so a shake or tilt threshold tuned on one host would read differently on the other. Author it in widget.native.jsx and pair it with a widget.web.jsx reading window.DeviceMotionEvent (accelerationIncludingGravity / rotationRate), the browser API the same hardware exposes. Both hosts need a user gesture before readings start, and iOS Safari additionally needs an explicit DeviceMotionEvent.requestPermission() grant — so gate the reading behind a Pressable, never start it on mount.",
|
|
2583
|
+
},
|
|
2574
2584
|
];
|
|
2575
2585
|
|
|
2576
2586
|
// sc-1064: CORE React infrastructure specifiers the host RESOLVES at runtime
|
|
@@ -3500,7 +3510,24 @@ const CONTRACT = deepFreeze({
|
|
|
3500
3510
|
// widget can open at SCREEN level. Everything before it was clipped by the
|
|
3501
3511
|
// layout container the widget sits in, so a preview or dialog could not leave
|
|
3502
3512
|
// the widget's own tile on either host.
|
|
3503
|
-
|
|
3513
|
+
// 1.89.0: additive — `expo-sensors` joins VETTED_IMPORTS as a native-only
|
|
3514
|
+
// `sensors` package (Accelerometer / Gyroscope / DeviceMotion / Barometer /
|
|
3515
|
+
// Pedometer / LightSensor). Widgets had no path to device motion at all: the
|
|
3516
|
+
// allowlist held no sensor package, so a step counter, a tilt/shake control,
|
|
3517
|
+
// a level, or a compass was unbuildable. Native-only because the package's
|
|
3518
|
+
// web build reads deviceorientation angles rather than real acceleration —
|
|
3519
|
+
// the web half is a widget.web.jsx over window.DeviceMotionEvent. Pinned in
|
|
3520
|
+
// the compiler's export dependencies, like every other native-module member.
|
|
3521
|
+
// 1.90.0: additive (sc-6750) — a `styleSchema` field's declared `default`
|
|
3522
|
+
// now RESOLVES onto `props.style`, as the weakest style layer. It was
|
|
3523
|
+
// written into the manifest and then read by nothing, so an author's saved
|
|
3524
|
+
// colour was silently dropped on the next render. Precedence is unchanged
|
|
3525
|
+
// above it: styleSchema `default` -> palette/`components.<scope>` ->
|
|
3526
|
+
// `widgetStyles[manifestId]` -> per-instance `props.style`, so a workspace
|
|
3527
|
+
// theme still outranks a widget's own baseline and a field the author never
|
|
3528
|
+
// defaulted follows the theme exactly as before. The host still never
|
|
3529
|
+
// applies style to elements — the widget owns placement.
|
|
3530
|
+
version: "1.90.0",
|
|
3504
3531
|
sharedTranslationKeys: SHARED_TRANSLATION_KEYS,
|
|
3505
3532
|
hooks: HOOKS,
|
|
3506
3533
|
primitives: PRIMITIVES,
|
package/dist/devserver.js
CHANGED
|
@@ -67,32 +67,38 @@ import {
|
|
|
67
67
|
shimSpecifierFromSlug,
|
|
68
68
|
} from "./dev-shims.js";
|
|
69
69
|
import { bundleWebEntry } from "./webbundle.js";
|
|
70
|
+
import {
|
|
71
|
+
findImportStatements,
|
|
72
|
+
findRelativeImportStatements,
|
|
73
|
+
} from "./flatten-entry.js";
|
|
70
74
|
|
|
71
75
|
// Bare-import / relative-import detection. The loader rewrites bare specifiers
|
|
72
76
|
// in the ENTRY to client-side blob shims, so they resolve fine. Relative
|
|
73
77
|
// imports in a SINGLE-FILE entry have nowhere to resolve — single-file mode
|
|
74
78
|
// refuses them with guidance. In directory mode, relative imports in the entry
|
|
75
79
|
// AND in siblings are rewritten to absolute dev-server URLs.
|
|
76
|
-
const RELATIVE_IMPORT_RE =
|
|
77
|
-
/(?:^|\n)\s*(?:import|export)[^;\n]*?from\s*['"](\.\.?\/[^'"]+)['"]/g;
|
|
78
|
-
|
|
79
|
-
// Static-import / static-export-from anchored at a statement boundary
|
|
80
|
-
// (start-of-line, `;`, or newline before `import`/`export`) so the rewriter
|
|
81
|
-
// doesn't touch occurrences inside string literals or comment bodies. The
|
|
82
|
-
// dynamic-import form (`import("...")`) is matched separately by
|
|
83
|
-
// `DYNAMIC_IMPORT_RE` below — it's safe to anchor in the same way because
|
|
84
|
-
// it's also valid only as an expression.
|
|
85
80
|
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
88
|
-
//
|
|
89
|
-
//
|
|
81
|
+
// STATIC imports are found by the packer's scanner (`findImportStatements`),
|
|
82
|
+
// never by a local regex. sc-6086 removed RELATIVE_IMPORT_RE and sc-6670 the
|
|
83
|
+
// rewriter's STATIC_IMPORT_RE for the same defect: a `[^'";\n]` clause body
|
|
84
|
+
// cannot span newlines, so a multi-line `import {\n a,\n} from "react"` — the
|
|
85
|
+
// norm in this codebase — read as no import at all and was served verbatim.
|
|
86
|
+
// The scanner walks the clause character by character, so line breaks are
|
|
87
|
+
// nothing special, and it returns the specifier's quote offsets so the
|
|
88
|
+
// rewriter splices exactly the specifier and can never bleed into an adjacent
|
|
89
|
+
// statement. One scanner for the guard, the packer, and the rewriter (§3).
|
|
90
|
+
|
|
91
|
+
// Dynamic import (`import("…")`) is an EXPRESSION, valid at any brace depth,
|
|
92
|
+
// so the statement scanner does not see it — it keeps its own regex. That
|
|
93
|
+
// regex separates the specifier from `import` with `\s*`, which does match
|
|
94
|
+
// newlines, so it has no multi-line gap (sc-6670 verified).
|
|
95
|
+
//
|
|
96
|
+
// Capture groups:
|
|
97
|
+
// 1 = `import` + whitespace + `(` (and the leading boundary char, preserved
|
|
98
|
+
// verbatim during replace so the call isn't glued onto what precedes it)
|
|
90
99
|
// 2 = opening quote
|
|
91
100
|
// 3 = specifier
|
|
92
|
-
const
|
|
93
|
-
/((?:^|[\n;])\s*(?:import|export)\s+(?:[^'";\n]*?\s+from\s*)?)(['"])([^'"]+)\2/gm;
|
|
94
|
-
const DYNAMIC_IMPORT_RE =
|
|
95
|
-
/((?:^|[\s;\(,!?:=])import\s*\(\s*)(['"])([^'"]+)\2/g;
|
|
101
|
+
const DYNAMIC_IMPORT_RE = /((?:^|[\s;\(,!?:=])import\s*\(\s*)(['"])([^'"]+)\2/g;
|
|
96
102
|
|
|
97
103
|
/**
|
|
98
104
|
* Lazily resolve `sucrase`'s `transform`. Kept out of the module's static
|
|
@@ -137,15 +143,21 @@ export function transpile(transform, source) {
|
|
|
137
143
|
* single-file mode to reject split-impl bundles with a clear message instead
|
|
138
144
|
* of serving a module the browser can't resolve.
|
|
139
145
|
*
|
|
146
|
+
* sc-6086: this delegates to the packer's statement scanner rather than
|
|
147
|
+
* `RELATIVE_IMPORT_RE`, whose `[^;\n]` body cannot span newlines — so a
|
|
148
|
+
* MULTI-LINE `import {\n a,\n b,\n} from "./lib/x.js"` was invisible to it.
|
|
149
|
+
* That blind spot is why `worktime-employer` passed every local check and
|
|
150
|
+
* still shipped an unresolvable import to the marketplace. One scanner, so
|
|
151
|
+
* the dev guard and the packer agree on what a relative import is
|
|
152
|
+
* (CLAUDE.md §3).
|
|
153
|
+
*
|
|
140
154
|
* @param {string} source
|
|
141
155
|
* @returns {string[]} unique relative specifiers
|
|
142
156
|
*/
|
|
143
157
|
export function findRelativeImports(source) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
while ((m = RELATIVE_IMPORT_RE.exec(source))) out.add(m[1]);
|
|
148
|
-
return Array.from(out);
|
|
158
|
+
return Array.from(
|
|
159
|
+
new Set(findRelativeImportStatements(source).map((s) => s.specifier)),
|
|
160
|
+
);
|
|
149
161
|
}
|
|
150
162
|
|
|
151
163
|
/**
|
|
@@ -201,15 +213,16 @@ export function resolveRelativeImport(fromRel, specifier) {
|
|
|
201
213
|
export function rewriteImportsForDirectoryMode(source, ctx) {
|
|
202
214
|
const { fromRel, baseUrl, shimmable } = ctx;
|
|
203
215
|
const unresolved = new Set();
|
|
204
|
-
|
|
216
|
+
// The URL a specifier should become, or null to leave it exactly as written.
|
|
217
|
+
const resolveSpecifier = (spec) => {
|
|
205
218
|
// Relative — resolve under the widget dir.
|
|
206
219
|
if (spec.startsWith("./") || spec.startsWith("../")) {
|
|
207
220
|
const rel = resolveRelativeImport(fromRel, spec);
|
|
208
221
|
if (!rel) {
|
|
209
222
|
unresolved.add(spec);
|
|
210
|
-
return
|
|
223
|
+
return null;
|
|
211
224
|
}
|
|
212
|
-
return `${
|
|
225
|
+
return `${baseUrl}/file/${rel}`;
|
|
213
226
|
}
|
|
214
227
|
// Absolute or already-resolved — leave alone.
|
|
215
228
|
if (
|
|
@@ -219,21 +232,36 @@ export function rewriteImportsForDirectoryMode(source, ctx) {
|
|
|
219
232
|
spec.startsWith("blob:") ||
|
|
220
233
|
spec.startsWith("data:")
|
|
221
234
|
) {
|
|
222
|
-
return
|
|
235
|
+
return null;
|
|
223
236
|
}
|
|
224
237
|
// Bare — rewrite if shimmable, else surface as unresolved.
|
|
225
238
|
if (shimmable.includes(spec)) {
|
|
226
|
-
return `${
|
|
239
|
+
return `${baseUrl}/shim/${shimSlugFor(spec)}`;
|
|
227
240
|
}
|
|
228
241
|
unresolved.add(spec);
|
|
229
|
-
return
|
|
242
|
+
return null;
|
|
230
243
|
};
|
|
231
|
-
|
|
232
|
-
//
|
|
233
|
-
//
|
|
234
|
-
//
|
|
235
|
-
|
|
236
|
-
|
|
244
|
+
|
|
245
|
+
// Pass 1 — static imports, spliced by the scanner's quote offsets. Splicing
|
|
246
|
+
// only what sits BETWEEN the quotes is what makes span bleed impossible:
|
|
247
|
+
// the clause, the trailing `;`, and any adjacent statement are never part of
|
|
248
|
+
// the replaced range. Walk back-to-front so earlier offsets stay valid.
|
|
249
|
+
const statements = findImportStatements(source);
|
|
250
|
+
let out = source;
|
|
251
|
+
for (let i = statements.length - 1; i >= 0; i -= 1) {
|
|
252
|
+
const { specifier, quoteStart, quoteEnd } = statements[i];
|
|
253
|
+
const url = resolveSpecifier(specifier);
|
|
254
|
+
if (url === null) continue;
|
|
255
|
+
out = out.slice(0, quoteStart + 1) + url + out.slice(quoteEnd);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Pass 2 — dynamic `import("…")`, an expression the statement scanner does
|
|
259
|
+
// not report. Its own anchor keeps it off substrings inside string literals
|
|
260
|
+
// like `const s = "import('react')"`.
|
|
261
|
+
out = out.replace(DYNAMIC_IMPORT_RE, (match, prefix, quote, spec) => {
|
|
262
|
+
const url = resolveSpecifier(spec);
|
|
263
|
+
return url === null ? match : `${prefix}${quote}${url}${quote}`;
|
|
264
|
+
});
|
|
237
265
|
return { code: out, unresolved: Array.from(unresolved) };
|
|
238
266
|
}
|
|
239
267
|
|
|
@@ -315,7 +343,10 @@ export function loadWidgetJson(widgetDir) {
|
|
|
315
343
|
const candidates = [
|
|
316
344
|
resolve(widgetDir, p),
|
|
317
345
|
p.startsWith(`first-party-widgets/${widgetSlug}/`)
|
|
318
|
-
? resolve(
|
|
346
|
+
? resolve(
|
|
347
|
+
widgetDir,
|
|
348
|
+
p.slice(`first-party-widgets/${widgetSlug}/`.length),
|
|
349
|
+
)
|
|
319
350
|
: null,
|
|
320
351
|
resolve(widgetDir, "..", "..", p),
|
|
321
352
|
].filter(Boolean);
|
|
@@ -326,7 +357,10 @@ export function loadWidgetJson(widgetDir) {
|
|
|
326
357
|
`${configPath}: ${label} must point at a file under ${widgetDir} (got "${p}")`,
|
|
327
358
|
);
|
|
328
359
|
}
|
|
329
|
-
const manifestAbs = _resolveUnderWidget(
|
|
360
|
+
const manifestAbs = _resolveUnderWidget(
|
|
361
|
+
"manifestSource",
|
|
362
|
+
config.manifestSource,
|
|
363
|
+
);
|
|
330
364
|
|
|
331
365
|
let entryAbs;
|
|
332
366
|
if (config.componentSources && typeof config.componentSources === "object") {
|
|
@@ -353,7 +387,12 @@ export function loadWidgetJson(widgetDir) {
|
|
|
353
387
|
);
|
|
354
388
|
}
|
|
355
389
|
const entryRel = relative(widgetDir, entryAbs).split(sep).join("/");
|
|
356
|
-
return {
|
|
390
|
+
return {
|
|
391
|
+
widgetDir,
|
|
392
|
+
manifestPath: manifestAbs,
|
|
393
|
+
entryPath: entryAbs,
|
|
394
|
+
entryRel,
|
|
395
|
+
};
|
|
357
396
|
}
|
|
358
397
|
|
|
359
398
|
function _isUnder(parentAbs, childAbs) {
|
|
@@ -444,7 +483,13 @@ export function createDevServer({
|
|
|
444
483
|
// entry's own ancestor node_modules by default, so a widget that vendored
|
|
445
484
|
// its deps locally still resolves.
|
|
446
485
|
const bundleNodePaths = [];
|
|
447
|
-
const _frontendNm = resolve(
|
|
486
|
+
const _frontendNm = resolve(
|
|
487
|
+
watchRoot,
|
|
488
|
+
"..",
|
|
489
|
+
"..",
|
|
490
|
+
"frontend",
|
|
491
|
+
"node_modules",
|
|
492
|
+
);
|
|
448
493
|
if (existsSync(_frontendNm)) bundleNodePaths.push(_frontendNm);
|
|
449
494
|
|
|
450
495
|
const sseClients = new Set();
|
|
@@ -700,7 +745,13 @@ export function createDevServer({
|
|
|
700
745
|
if (url === "/__dev/events") return serveEvents(req, res);
|
|
701
746
|
if (url === "/__dev/health") {
|
|
702
747
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
703
|
-
res.end(
|
|
748
|
+
res.end(
|
|
749
|
+
JSON.stringify({
|
|
750
|
+
ok: true,
|
|
751
|
+
manifestId,
|
|
752
|
+
mode: directoryMode ? "directory" : "single-file",
|
|
753
|
+
}),
|
|
754
|
+
);
|
|
704
755
|
return;
|
|
705
756
|
}
|
|
706
757
|
if (directoryMode && url.startsWith("/file/")) {
|
|
@@ -736,7 +787,8 @@ export function createDevServer({
|
|
|
736
787
|
if (ok) onLint("lint: clean");
|
|
737
788
|
else {
|
|
738
789
|
onLint(`lint: ${findings.length} finding(s)`);
|
|
739
|
-
for (const f of findings)
|
|
790
|
+
for (const f of findings)
|
|
791
|
+
onLint(` [${f.rule}] line ${f.line}: ${f.label}`);
|
|
740
792
|
}
|
|
741
793
|
}
|
|
742
794
|
}
|