@torpor/view 0.4.12 → 0.4.14
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/CHANGELOG.md +16 -0
- package/dist/{Cleanup-BGQ5zaxE.d.cts → Cleanup-CYsytTEc.d.cts} +1 -1
- package/dist/Cleanup-CYsytTEc.d.cts.map +1 -0
- package/dist/{Cleanup-Ca8IspaG.d.ts → Cleanup-CgjyN0lW.d.mts} +1 -1
- package/dist/Cleanup-CgjyN0lW.d.mts.map +1 -0
- package/dist/{Component-BDUCwkBQ.d.cts → Component-DmWGMgak.d.mts} +1 -1
- package/dist/{Component-BDUCwkBQ.d.cts.map → Component-DmWGMgak.d.mts.map} +1 -1
- package/dist/{Component-JVwSemf-.d.ts → Component-kGLRFYg2.d.cts} +1 -1
- package/dist/Component-kGLRFYg2.d.cts.map +1 -0
- package/dist/{Region-BrsZLZUe.d.ts → Region-Bb6HAb-F.d.cts} +2 -2
- package/dist/{Region-Cf7OeBIQ.d.cts.map → Region-Bb6HAb-F.d.cts.map} +1 -1
- package/dist/{Region-Cf7OeBIQ.d.cts → Region-CTchywkr.d.mts} +2 -2
- package/dist/Region-CTchywkr.d.mts.map +1 -0
- package/dist/compile.cjs +239 -71
- package/dist/compile.d.cts +1 -1
- package/dist/{compile.d.ts → compile.d.mts} +2 -2
- package/dist/compile.d.mts.map +1 -0
- package/dist/{compile.js → compile.mjs} +239 -70
- package/dist/compile.mjs.map +1 -0
- package/dist/dev.cjs +1 -2
- package/dist/dev.d.cts +2 -2
- package/dist/{dev.d.ts → dev.d.mts} +3 -3
- package/dist/dev.d.mts.map +1 -0
- package/dist/{dev.js → dev.mjs} +2 -2
- package/dist/{dev.cjs.map → dev.mjs.map} +1 -1
- package/dist/devContext-B3yskfhA.cjs +1 -2
- package/dist/{devContext-CevrTlUO.js → devContext-CQ8RhBX-.mjs} +1 -1
- package/dist/{devContext-B3yskfhA.cjs.map → devContext-CQ8RhBX-.mjs.map} +1 -1
- package/dist/{formatText-Ck04q8_X.cjs → formatText--9zsK5Nq.cjs} +4 -4
- package/dist/{formatText-OyD8MNAM.js → formatText-B8u71S_K.mjs} +4 -3
- package/dist/formatText-B8u71S_K.mjs.map +1 -0
- package/dist/formatText-BNP3P38F.d.cts +17 -0
- package/dist/formatText-BNP3P38F.d.cts.map +1 -0
- package/dist/formatText-D6Ov8Ub0.d.mts +17 -0
- package/dist/formatText-D6Ov8Ub0.d.mts.map +1 -0
- package/dist/index.cjs +2 -3
- package/dist/index.d.cts +5 -5
- package/dist/{index.d.ts → index.d.mts} +6 -6
- package/dist/index.d.mts.map +1 -0
- package/dist/{index.js → index.mjs} +3 -3
- package/dist/{index.cjs.map → index.mjs.map} +1 -1
- package/dist/ssr.cjs +2 -3
- package/dist/ssr.d.cts +2 -2
- package/dist/{ssr.d.ts → ssr.d.mts} +3 -3
- package/dist/ssr.d.mts.map +1 -0
- package/dist/{ssr.js → ssr.mjs} +2 -2
- package/dist/{ssr.cjs.map → ssr.mjs.map} +1 -1
- package/package.json +17 -17
- package/scripts/copyToRepl.ts +6 -6
- package/src/compile/build/client/addMappedTextWithOffsets.ts +31 -0
- package/src/compile/build/client/buildAddFragment.ts +11 -11
- package/src/compile/build/client/buildComponentNode.ts +123 -29
- package/src/compile/build/client/buildElementNode.ts +65 -8
- package/src/compile/build/client/buildIfNode.ts +1 -1
- package/src/compile/build/client/buildSwitchNode.ts +1 -1
- package/src/compile/build/client/buildTextNode.ts +13 -6
- package/src/compile/build/client/getAttributeOffsets.ts +90 -0
- package/src/compile/build/client/stashRunWithOffsets.ts +7 -1
- package/src/compile/build/server/buildServerElementNode.ts +1 -1
- package/src/compile/parse/parseTag.ts +1 -7
- package/src/compile/parse/parseText.ts +1 -1
- package/src/index.ts +3 -1
- package/src/render/buildClasses.ts +4 -17
- package/src/render/buildStyles.ts +6 -4
- package/src/types/ClassValue.ts +8 -0
- package/src/types/StyleValue.ts +8 -0
- package/test/source-maps/interpolated.test.ts +88 -0
- package/test/source-maps/parse.test.ts +1 -1
- package/tsdown.config.ts +2 -9
- package/vitest.config.ts +3 -3
- package/dist/Cleanup-BGQ5zaxE.d.cts.map +0 -1
- package/dist/Cleanup-Ca8IspaG.d.ts.map +0 -1
- package/dist/Component-JVwSemf-.d.ts.map +0 -1
- package/dist/Region-BrsZLZUe.d.ts.map +0 -1
- package/dist/compile.cjs.map +0 -1
- package/dist/compile.d.ts.map +0 -1
- package/dist/compile.js.map +0 -1
- package/dist/dev.d.ts.map +0 -1
- package/dist/dev.js.map +0 -1
- package/dist/devContext-CevrTlUO.js.map +0 -1
- package/dist/formatText-A8G0kbCI.d.ts +0 -11
- package/dist/formatText-A8G0kbCI.d.ts.map +0 -1
- package/dist/formatText-Ck04q8_X.cjs.map +0 -1
- package/dist/formatText-DNPkZ1xY.d.cts +0 -11
- package/dist/formatText-DNPkZ1xY.d.cts.map +0 -1
- package/dist/formatText-OyD8MNAM.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/ssr.d.ts.map +0 -1
- package/dist/ssr.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@torpor/view",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.14",
|
|
4
4
|
"description": "Torpor's view library, for writing and mounting components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
8
|
-
"types": "./dist/index.d.
|
|
9
|
-
"import": "./dist/index.
|
|
8
|
+
"types": "./dist/index.d.mts",
|
|
9
|
+
"import": "./dist/index.mjs",
|
|
10
10
|
"require": "./dist/index.cjs"
|
|
11
11
|
},
|
|
12
12
|
"./compile": {
|
|
13
|
-
"types": "./dist/compile.d.
|
|
14
|
-
"import": "./dist/compile.
|
|
13
|
+
"types": "./dist/compile.d.mts",
|
|
14
|
+
"import": "./dist/compile.mjs",
|
|
15
15
|
"require": "./dist/compile.cjs"
|
|
16
16
|
},
|
|
17
17
|
"./ssr": {
|
|
18
|
-
"types": "./dist/ssr.d.
|
|
19
|
-
"import": "./dist/ssr.
|
|
18
|
+
"types": "./dist/ssr.d.mts",
|
|
19
|
+
"import": "./dist/ssr.mjs",
|
|
20
20
|
"require": "./dist/ssr.cjs"
|
|
21
21
|
},
|
|
22
22
|
"./dev": {
|
|
23
|
-
"types": "./dist/dev.d.
|
|
24
|
-
"import": "./dist/dev.
|
|
23
|
+
"types": "./dist/dev.d.mts",
|
|
24
|
+
"import": "./dist/dev.mjs",
|
|
25
25
|
"require": "./dist/dev.cjs"
|
|
26
26
|
}
|
|
27
27
|
},
|
|
@@ -35,24 +35,24 @@
|
|
|
35
35
|
"@testing-library/dom": "^10.4.1",
|
|
36
36
|
"@testing-library/jest-dom": "^6.9.1",
|
|
37
37
|
"@testing-library/user-event": "^14.6.1",
|
|
38
|
-
"@trivago/prettier-plugin-sort-imports": "^
|
|
38
|
+
"@trivago/prettier-plugin-sort-imports": "^6.0.0",
|
|
39
39
|
"@types/jsdom": "^27.0.0",
|
|
40
|
-
"@types/node": "^24.10.
|
|
41
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
40
|
+
"@types/node": "^24.10.1",
|
|
41
|
+
"@typescript/native-preview": "7.0.0-dev.20251112.1",
|
|
42
42
|
"fast-glob": "^3.3.3",
|
|
43
|
-
"jsdom": "^27.
|
|
44
|
-
"oxlint-tsgolint": "^0.
|
|
43
|
+
"jsdom": "^27.2.0",
|
|
44
|
+
"oxlint-tsgolint": "^0.6.0",
|
|
45
45
|
"sucrase": "^3.35.0",
|
|
46
46
|
"tinybench": "^5.1.0",
|
|
47
|
-
"tsdown": "^0.
|
|
47
|
+
"tsdown": "^0.16.4",
|
|
48
48
|
"tsx": "^4.20.6",
|
|
49
49
|
"typescript": "^5.9.3",
|
|
50
|
-
"vitest": "^4.0.
|
|
50
|
+
"vitest": "^4.0.8"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
|
+
"check": "tsgo --noEmit && pnpm dlx oxlint --type-aware",
|
|
53
54
|
"build": "tsgo --noEmit && tsdown && tsx ./scripts/copyToRepl.ts",
|
|
54
55
|
"build:watch": "tsdown --watch",
|
|
55
|
-
"check": "tsgo --noEmit && pnpm dlx oxlint --type-aware",
|
|
56
56
|
"test": "vitest",
|
|
57
57
|
"test:build": "tsx ./scripts/buildTestOutput.ts",
|
|
58
58
|
"bench": "tsx ./bench/bench.ts",
|
package/scripts/copyToRepl.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { promises as fs, readFileSync } from "node:fs";
|
|
2
2
|
|
|
3
3
|
async function run() {
|
|
4
|
-
const indexFile = "./dist/index.
|
|
4
|
+
const indexFile = "./dist/index.mjs";
|
|
5
5
|
let source = await fs.readFile(indexFile, "utf8");
|
|
6
6
|
source = source
|
|
7
7
|
.replace(/^export \{(.+)\};/gms, (_, capture) => {
|
|
@@ -19,13 +19,13 @@ async function run() {
|
|
|
19
19
|
);
|
|
20
20
|
})
|
|
21
21
|
// HACK: Why are these imported?
|
|
22
|
-
.replaceAll(/^import \{.+} from ".\/(.+?).
|
|
23
|
-
const importedFile = `./dist/${capture}.
|
|
22
|
+
.replaceAll(/^import \{.+} from ".\/(.+?).mjs";/gm, (_, capture) => {
|
|
23
|
+
const importedFile = `./dist/${capture}.mjs`;
|
|
24
24
|
let importedSource = readFileSync(importedFile, "utf8");
|
|
25
|
-
return importedSource.replace(/^export \{(.+)\};/
|
|
25
|
+
return importedSource.replace(/^export \{(.+)\};/gm, "");
|
|
26
26
|
})
|
|
27
|
-
.replaceAll(/^import ".\/(.+?).
|
|
28
|
-
.replaceAll(/^\/\/.+?$/
|
|
27
|
+
.replaceAll(/^import ".\/(.+?).mjs";/gm, "")
|
|
28
|
+
.replaceAll(/^\/\/.+?$/gm, "")
|
|
29
29
|
.replaceAll(/^\/\*.+?^\*\//gms, "")
|
|
30
30
|
.replaceAll(/\n+/g, "\n");
|
|
31
31
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type SourceSpan from "../../types/SourceSpan";
|
|
2
|
+
import Builder from "../../utils/Builder";
|
|
3
|
+
import type BuildStatus from "./BuildStatus";
|
|
4
|
+
|
|
5
|
+
export default function addMappedTextWithOffsets(
|
|
6
|
+
preText: string,
|
|
7
|
+
text: string,
|
|
8
|
+
postText: string,
|
|
9
|
+
spans: SourceSpan[],
|
|
10
|
+
offsets: number[],
|
|
11
|
+
lengths: number[],
|
|
12
|
+
status: BuildStatus,
|
|
13
|
+
b: Builder,
|
|
14
|
+
): void {
|
|
15
|
+
if (status.options.mapped === true) {
|
|
16
|
+
let mappedStart = b.toString().length + preText.length;
|
|
17
|
+
let functionBody = preText + text + postText;
|
|
18
|
+
b.append(functionBody);
|
|
19
|
+
for (let i = 0; i < spans.length; i++) {
|
|
20
|
+
let start = mappedStart + offsets[i];
|
|
21
|
+
let end = start + lengths[i];
|
|
22
|
+
status.map.push({
|
|
23
|
+
script: functionBody,
|
|
24
|
+
source: spans[i],
|
|
25
|
+
compiled: { start, end },
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
} else {
|
|
29
|
+
b.append(preText + text + postText);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -3,6 +3,7 @@ import type ElementNode from "../../types/nodes/ElementNode";
|
|
|
3
3
|
import type RootNode from "../../types/nodes/RootNode";
|
|
4
4
|
import Builder from "../../utils/Builder";
|
|
5
5
|
import type BuildStatus from "./BuildStatus";
|
|
6
|
+
import addMappedTextWithOffsets from "./addMappedTextWithOffsets";
|
|
6
7
|
|
|
7
8
|
export default function buildAddFragment(
|
|
8
9
|
node: RootNode | ControlNode | ElementNode,
|
|
@@ -16,21 +17,20 @@ export default function buildAddFragment(
|
|
|
16
17
|
const fragmentName = `t_fragment_${fragment.number}`;
|
|
17
18
|
status.imports.add("t_add_fragment");
|
|
18
19
|
if (fragment.effects.length > 0) {
|
|
19
|
-
// TODO: nicer mapping
|
|
20
20
|
status.imports.add("$run");
|
|
21
21
|
b.append("$run(() => {");
|
|
22
22
|
if (status.options.mapped === true) {
|
|
23
23
|
for (let effect of fragment.effects) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
24
|
+
addMappedTextWithOffsets(
|
|
25
|
+
"",
|
|
26
|
+
effect.functionBody,
|
|
27
|
+
"",
|
|
28
|
+
effect.spans,
|
|
29
|
+
effect.offsets,
|
|
30
|
+
effect.lengths,
|
|
31
|
+
status,
|
|
32
|
+
b,
|
|
33
|
+
);
|
|
34
34
|
}
|
|
35
35
|
} else {
|
|
36
36
|
b.append(fragment.effects.map((e) => e.functionBody).join("\n"));
|
|
@@ -6,10 +6,12 @@ import trimQuotes from "../../utils/trimQuotes";
|
|
|
6
6
|
import nextVarName from "../utils/nextVarName";
|
|
7
7
|
import type BuildStatus from "./BuildStatus";
|
|
8
8
|
import addMappedText from "./addMappedText";
|
|
9
|
+
import addMappedTextWithOffsets from "./addMappedTextWithOffsets";
|
|
9
10
|
import buildAddFragment from "./buildAddFragment";
|
|
10
11
|
import buildFragment from "./buildFragment";
|
|
11
12
|
import buildNode from "./buildNode";
|
|
12
13
|
import buildRun from "./buildRun";
|
|
14
|
+
import getAttributeOffsets from "./getAttributeOffsets";
|
|
13
15
|
import replaceForVarNames from "./replaceForVarNames";
|
|
14
16
|
|
|
15
17
|
export default function buildComponentNode(
|
|
@@ -34,44 +36,123 @@ export default function buildComponentNode(
|
|
|
34
36
|
status.imports.add("$watch");
|
|
35
37
|
|
|
36
38
|
// Gather props, runs and binding runs, and set them all at once
|
|
37
|
-
let props: {
|
|
39
|
+
let props: {
|
|
40
|
+
name: string;
|
|
41
|
+
// Text to be ignored in the mapping e.g. `t_class(`
|
|
42
|
+
preText: string;
|
|
43
|
+
value: string;
|
|
44
|
+
// Text to be ignored in the mapping e.g. `)`
|
|
45
|
+
postText: string;
|
|
46
|
+
spans: SourceSpan[];
|
|
47
|
+
offsets: number[];
|
|
48
|
+
lengths: number[];
|
|
49
|
+
}[] = [];
|
|
38
50
|
let runs: string[] = [];
|
|
39
51
|
let bindingRuns: string[] = [];
|
|
40
52
|
|
|
41
53
|
for (let { name, value, reactive, fullyReactive, span } of node.attributes) {
|
|
42
54
|
if (name === "self" && node.tagName === "@component") {
|
|
43
55
|
// Ignore this special attribute
|
|
44
|
-
} else if (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
} else if (value != null && fullyReactive) {
|
|
57
|
+
if (name.startsWith("&")) {
|
|
58
|
+
// It's a bound property
|
|
59
|
+
// Add two $runs -- one to update the props, and one to update the value
|
|
60
|
+
// The component author will need to make sure $props.x is updated
|
|
61
|
+
// TODO: Maybe allow adding a Bindable<T> type to the $props interface??
|
|
62
|
+
// e.g. Component($props: { text: Bindable<string> })
|
|
63
|
+
name = name.substring(1);
|
|
64
|
+
props.push({
|
|
65
|
+
name,
|
|
66
|
+
preText: "",
|
|
67
|
+
value,
|
|
68
|
+
postText: "",
|
|
69
|
+
spans: [span],
|
|
70
|
+
offsets: [],
|
|
71
|
+
lengths: [],
|
|
72
|
+
});
|
|
73
|
+
runs.push(`${propsName}["${name}"] = ${value};`);
|
|
74
|
+
bindingRuns.push(`${value} = ${propsName}["${name}"];`);
|
|
75
|
+
} else if (name === "class") {
|
|
76
|
+
status.imports.add("t_class");
|
|
77
|
+
const params = [value];
|
|
78
|
+
if (node.scopeStyles) {
|
|
79
|
+
params.push(`"torp-${status.styleHash}"`);
|
|
80
|
+
}
|
|
81
|
+
value = params.join(", ");
|
|
82
|
+
props.push({
|
|
83
|
+
name,
|
|
84
|
+
preText: "t_class(",
|
|
85
|
+
value,
|
|
86
|
+
postText: ")",
|
|
87
|
+
spans: [span],
|
|
88
|
+
offsets: [],
|
|
89
|
+
lengths: [],
|
|
90
|
+
});
|
|
91
|
+
runs.push(`${propsName}["${name}"] = t_class(${value});`);
|
|
92
|
+
} else if (name === "style") {
|
|
93
|
+
status.imports.add("t_style");
|
|
94
|
+
props.push({
|
|
95
|
+
name,
|
|
96
|
+
preText: "t_style(",
|
|
97
|
+
value,
|
|
98
|
+
postText: ")",
|
|
99
|
+
spans: [span],
|
|
100
|
+
offsets: [],
|
|
101
|
+
lengths: [],
|
|
102
|
+
});
|
|
103
|
+
runs.push(`${propsName}["${name}"] = t_style(${value});`);
|
|
104
|
+
} else {
|
|
105
|
+
props.push({
|
|
106
|
+
name,
|
|
107
|
+
preText: "",
|
|
108
|
+
value,
|
|
109
|
+
postText: "",
|
|
110
|
+
spans: [span],
|
|
111
|
+
offsets: [],
|
|
112
|
+
lengths: [],
|
|
113
|
+
});
|
|
114
|
+
runs.push(`${propsName}["${name}"] = ${value};`);
|
|
59
115
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
116
|
+
} else if (value != null && reactive) {
|
|
117
|
+
const { newValue, spans, offsets, lengths } = getAttributeOffsets(value, span);
|
|
118
|
+
value = newValue;
|
|
119
|
+
if (name === "class") {
|
|
120
|
+
status.imports.add("t_class");
|
|
121
|
+
const params = [value];
|
|
122
|
+
if (node.scopeStyles) {
|
|
123
|
+
params.push(`"torp-${status.styleHash}"`);
|
|
124
|
+
}
|
|
125
|
+
value = params.join(", ");
|
|
126
|
+
props.push({ name, preText: "t_class(", value, postText: ")", spans, offsets, lengths });
|
|
127
|
+
runs.push(`${propsName}["${name}"] = t_class(${value});`);
|
|
128
|
+
} else if (name === "style") {
|
|
129
|
+
status.imports.add("t_style");
|
|
130
|
+
props.push({ name, preText: "t_style(", value, postText: ")", spans, offsets, lengths });
|
|
131
|
+
runs.push(`${propsName}["${name}"] = t_style(${value});`);
|
|
132
|
+
} else {
|
|
133
|
+
props.push({ name, preText: "", value, postText: "", spans, offsets, lengths });
|
|
71
134
|
runs.push(`${propsName}["${name}"] = ${value};`);
|
|
72
135
|
}
|
|
136
|
+
} else if (value != null) {
|
|
137
|
+
props.push({
|
|
138
|
+
name,
|
|
139
|
+
preText: "",
|
|
140
|
+
value,
|
|
141
|
+
postText: "",
|
|
142
|
+
spans: [span],
|
|
143
|
+
offsets: [],
|
|
144
|
+
lengths: [],
|
|
145
|
+
});
|
|
73
146
|
} else {
|
|
74
|
-
props.push({
|
|
147
|
+
props.push({
|
|
148
|
+
name,
|
|
149
|
+
preText: "",
|
|
150
|
+
value: "true",
|
|
151
|
+
postText: "",
|
|
152
|
+
spans: [span],
|
|
153
|
+
offsets: [],
|
|
154
|
+
lengths: [],
|
|
155
|
+
});
|
|
75
156
|
}
|
|
76
157
|
}
|
|
77
158
|
|
|
@@ -79,7 +160,20 @@ export default function buildComponentNode(
|
|
|
79
160
|
b.append(`const ${propsName} = $watch({`);
|
|
80
161
|
for (let p of props) {
|
|
81
162
|
let value = replaceForVarNames(p.value, status);
|
|
82
|
-
|
|
163
|
+
if (p.spans.length === 1) {
|
|
164
|
+
addMappedText(`${p.name}: ` + p.preText, value, p.postText + ",", p.spans[0], status, b);
|
|
165
|
+
} else {
|
|
166
|
+
addMappedTextWithOffsets(
|
|
167
|
+
`${p.name}: ` + p.preText,
|
|
168
|
+
value,
|
|
169
|
+
p.postText + ",",
|
|
170
|
+
p.spans,
|
|
171
|
+
p.offsets,
|
|
172
|
+
p.lengths,
|
|
173
|
+
status,
|
|
174
|
+
b,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
83
177
|
}
|
|
84
178
|
b.append("});");
|
|
85
179
|
// TODO: Map these things:
|
|
@@ -12,8 +12,10 @@ import buildFragment from "./buildFragment";
|
|
|
12
12
|
import buildMount from "./buildMount";
|
|
13
13
|
import buildNode from "./buildNode";
|
|
14
14
|
import buildRun from "./buildRun";
|
|
15
|
+
import getAttributeOffsets from "./getAttributeOffsets";
|
|
15
16
|
import replaceForVarNames from "./replaceForVarNames";
|
|
16
17
|
import stashRun from "./stashRun";
|
|
18
|
+
import stashRunWithOffsets from "./stashRunWithOffsets";
|
|
17
19
|
|
|
18
20
|
export default function buildElementNode(
|
|
19
21
|
node: ElementNode,
|
|
@@ -178,12 +180,12 @@ function buildElementAttributes(
|
|
|
178
180
|
}
|
|
179
181
|
}
|
|
180
182
|
|
|
181
|
-
for (let { name, value, reactive, span
|
|
183
|
+
for (let { name, value, reactive, fullyReactive, span } of node.attributes) {
|
|
182
184
|
if (name === "self" && node.tagName === "@element") {
|
|
183
185
|
// Ignore this special attribute
|
|
184
186
|
} else if (name === "&ref") {
|
|
185
187
|
// Ignore this one, it should have been done already, above
|
|
186
|
-
} else if (value != null &&
|
|
188
|
+
} else if (value != null && fullyReactive) {
|
|
187
189
|
if (name === "&group") {
|
|
188
190
|
buildBindGroupAttribute(node, varName, value, status, b);
|
|
189
191
|
} else if (name === "&value" || name === "&checked") {
|
|
@@ -194,7 +196,7 @@ function buildElementAttributes(
|
|
|
194
196
|
// returning a cleanup function
|
|
195
197
|
buildMount("elMount", `return (${trimEnd(value.trim(), ";")})(${varName});`, status, b);
|
|
196
198
|
} else if (name.startsWith("on")) {
|
|
197
|
-
buildEventAttribute(varName, name, value,
|
|
199
|
+
buildEventAttribute(varName, name, value, span, status, b);
|
|
198
200
|
} else if (name.startsWith("transition")) {
|
|
199
201
|
buildTransitionAttribute(node, varName, name, value, status, b);
|
|
200
202
|
} else if (name === "class") {
|
|
@@ -210,29 +212,84 @@ function buildElementAttributes(
|
|
|
210
212
|
`${status.ns ? "// @ts-ignore\n" : ""}${varName}.${propName} = t_class(`,
|
|
211
213
|
params.join(", "),
|
|
212
214
|
");",
|
|
213
|
-
|
|
215
|
+
span,
|
|
214
216
|
status,
|
|
215
217
|
);
|
|
216
218
|
} else if (name === "style") {
|
|
217
219
|
status.imports.add("t_style");
|
|
218
|
-
stashRun(
|
|
220
|
+
stashRun(fragment, `${varName}.setAttribute("style", t_style(`, value, "));", span, status);
|
|
221
|
+
} else if (name.includes("-")) {
|
|
222
|
+
// Handle data-, aria- etc
|
|
223
|
+
status.imports.add("t_attribute");
|
|
224
|
+
stashRun(fragment, `t_attribute(${varName}, "${name}", `, value, ");", span, status);
|
|
225
|
+
// NOTE: dataset seems to be a tiny bit slower?
|
|
226
|
+
//const propName = name.substring(name.indexOf("-"));
|
|
227
|
+
//buildRun("setDataAttribute", `${varName}.dataset.${propName} = ${value};`, status, b);
|
|
228
|
+
} else {
|
|
229
|
+
status.imports.add("t_attribute");
|
|
230
|
+
stashRun(fragment, `t_attribute(${varName}, "${name}", `, value, ");", span, status);
|
|
231
|
+
}
|
|
232
|
+
} else if (value != null && reactive) {
|
|
233
|
+
const { newValue, spans, offsets, lengths } = getAttributeOffsets(value, span);
|
|
234
|
+
value = newValue;
|
|
235
|
+
if (name === "class") {
|
|
236
|
+
status.imports.add("t_class");
|
|
237
|
+
const params = [value];
|
|
238
|
+
if (node.scopeStyles) {
|
|
239
|
+
params.push(`"torp-${status.styleHash}"`);
|
|
240
|
+
}
|
|
241
|
+
// SVGs have a different className
|
|
242
|
+
const propName = status.ns ? "className.baseVal" : "className";
|
|
243
|
+
stashRunWithOffsets(
|
|
244
|
+
fragment,
|
|
245
|
+
`${status.ns ? "// @ts-ignore\n" : ""}${varName}.${propName} = t_class(`,
|
|
246
|
+
params.join(", "),
|
|
247
|
+
");",
|
|
248
|
+
spans,
|
|
249
|
+
offsets,
|
|
250
|
+
lengths,
|
|
251
|
+
status,
|
|
252
|
+
);
|
|
253
|
+
} else if (name === "style") {
|
|
254
|
+
status.imports.add("t_style");
|
|
255
|
+
stashRunWithOffsets(
|
|
219
256
|
fragment,
|
|
220
257
|
`${varName}.setAttribute("style", t_style(`,
|
|
221
258
|
value,
|
|
222
259
|
"));",
|
|
223
|
-
|
|
260
|
+
spans,
|
|
261
|
+
offsets,
|
|
262
|
+
lengths,
|
|
224
263
|
status,
|
|
225
264
|
);
|
|
226
265
|
} else if (name.includes("-")) {
|
|
227
266
|
// Handle data-, aria- etc
|
|
228
267
|
status.imports.add("t_attribute");
|
|
229
|
-
|
|
268
|
+
stashRunWithOffsets(
|
|
269
|
+
fragment,
|
|
270
|
+
`t_attribute(${varName}, "${name}", `,
|
|
271
|
+
value,
|
|
272
|
+
");",
|
|
273
|
+
spans,
|
|
274
|
+
offsets,
|
|
275
|
+
lengths,
|
|
276
|
+
status,
|
|
277
|
+
);
|
|
230
278
|
// NOTE: dataset seems to be a tiny bit slower?
|
|
231
279
|
//const propName = name.substring(name.indexOf("-"));
|
|
232
280
|
//buildRun("setDataAttribute", `${varName}.dataset.${propName} = ${value};`, status, b);
|
|
233
281
|
} else {
|
|
234
282
|
status.imports.add("t_attribute");
|
|
235
|
-
|
|
283
|
+
stashRunWithOffsets(
|
|
284
|
+
fragment,
|
|
285
|
+
`t_attribute(${varName}, "${name}", `,
|
|
286
|
+
value,
|
|
287
|
+
");",
|
|
288
|
+
spans,
|
|
289
|
+
offsets,
|
|
290
|
+
lengths,
|
|
291
|
+
status,
|
|
292
|
+
);
|
|
236
293
|
}
|
|
237
294
|
}
|
|
238
295
|
}
|
|
@@ -48,7 +48,7 @@ export default function buildIfNode(node: ControlNode, status: BuildStatus, b: B
|
|
|
48
48
|
b.append(`
|
|
49
49
|
const ${regionName} = t_region(${status.options.dev === true ? `"if"` : ""});
|
|
50
50
|
let ${indexName} = -1;
|
|
51
|
-
t_run_control(${regionName}, ${anchorName}, (t_before) => {`);
|
|
51
|
+
t_run_control(${regionName}, ${anchorName}, (${status.inHead ? "" : "t_before"}) => {`);
|
|
52
52
|
|
|
53
53
|
let index = 0;
|
|
54
54
|
for (let branch of branches) {
|
|
@@ -46,7 +46,7 @@ export default function buildSwitchNode(node: ControlNode, status: BuildStatus,
|
|
|
46
46
|
b.append(`
|
|
47
47
|
const ${regionName} = t_region(${status.options.dev === true ? `"switch"` : ""});
|
|
48
48
|
let ${indexName} = -1;
|
|
49
|
-
t_run_control(${regionName}, ${anchorName}, (t_before) => {`);
|
|
49
|
+
t_run_control(${regionName}, ${anchorName}, (${status.inHead ? "" : "t_before"}) => {`);
|
|
50
50
|
|
|
51
51
|
// TODO: replaceForVarNames is going to throw mapping out
|
|
52
52
|
addMappedText("", `${replaceForVarNames(node.statement, status)}`, " {", node.span, status, b);
|
|
@@ -16,8 +16,8 @@ export default function buildTextNode(node: TextNode, status: BuildStatus): void
|
|
|
16
16
|
content = content.replaceAll(/\s+/g, " ");
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
// TODO:
|
|
20
|
-
//
|
|
19
|
+
// TODO: Should we be parsing out the spans here? Like we do with
|
|
20
|
+
// attributes? And should we merge this with the attributes logic?
|
|
21
21
|
let textContent = "";
|
|
22
22
|
let reactiveCount = 0;
|
|
23
23
|
let reactiveStart = 0;
|
|
@@ -90,10 +90,17 @@ export default function buildTextNode(node: TextNode, status: BuildStatus): void
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
status.imports.add("t_fmt");
|
|
93
|
-
let functionBody = `${node.varName}.textContent = `;
|
|
94
|
-
offsetChange += functionBody.length;
|
|
95
93
|
offsets.map((_, i) => (offsets[i] += offsetChange));
|
|
96
|
-
|
|
97
|
-
stashRunWithOffsets(
|
|
94
|
+
|
|
95
|
+
stashRunWithOffsets(
|
|
96
|
+
fragment,
|
|
97
|
+
`${node.varName}.textContent = `,
|
|
98
|
+
textContent,
|
|
99
|
+
";",
|
|
100
|
+
node.spans,
|
|
101
|
+
offsets,
|
|
102
|
+
lengths,
|
|
103
|
+
status,
|
|
104
|
+
);
|
|
98
105
|
}
|
|
99
106
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import type SourceSpan from "../../types/SourceSpan";
|
|
2
|
+
import endOfString from "../../utils/endOfString";
|
|
3
|
+
import endOfTemplateString from "../../utils/endOfTemplateString";
|
|
4
|
+
import trimQuotes from "../../utils/trimQuotes";
|
|
5
|
+
|
|
6
|
+
export default function getAttributeOffsets(
|
|
7
|
+
value: string,
|
|
8
|
+
span: SourceSpan,
|
|
9
|
+
): {
|
|
10
|
+
newValue: string;
|
|
11
|
+
spans: SourceSpan[];
|
|
12
|
+
offsets: number[];
|
|
13
|
+
lengths: number[];
|
|
14
|
+
} {
|
|
15
|
+
// Build offsets for interpolated values e.g. attribute="value {x}
|
|
16
|
+
// and {y}" should have offsets for where the x and y are
|
|
17
|
+
// interpolated into the output
|
|
18
|
+
value = trimQuotes(value);
|
|
19
|
+
let spans: SourceSpan[] = [];
|
|
20
|
+
let offsets: number[] = [];
|
|
21
|
+
let lengths: number[] = [];
|
|
22
|
+
|
|
23
|
+
let textContent = "";
|
|
24
|
+
let reactiveStart = 0;
|
|
25
|
+
let level = 0;
|
|
26
|
+
for (let i = 0; i < value.length; i++) {
|
|
27
|
+
const char = value[i];
|
|
28
|
+
const nextChar = value[i + 1];
|
|
29
|
+
if (char === "{") {
|
|
30
|
+
level++;
|
|
31
|
+
//maxLevel = Math.max(level, maxLevel);
|
|
32
|
+
if (level === 1) {
|
|
33
|
+
//reactiveCount += 1;
|
|
34
|
+
textContent += "${";
|
|
35
|
+
reactiveStart = textContent.length;
|
|
36
|
+
// 2 is for the start quote plus the new `$`
|
|
37
|
+
spans.push({ start: span.start + i + 2, end: -1 });
|
|
38
|
+
offsets.push(reactiveStart + 1);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
} else if (char === "}") {
|
|
42
|
+
level--;
|
|
43
|
+
if (level === 0) {
|
|
44
|
+
// 2 is for the start quote plus the new `$`
|
|
45
|
+
spans.at(-1)!.end = span.start + i + 2;
|
|
46
|
+
lengths.push(textContent.length - reactiveStart);
|
|
47
|
+
textContent += "}";
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
} else if (char === "`" && level === 0) {
|
|
51
|
+
// Escape backticks outside of braces, as they will be within a backtick string
|
|
52
|
+
textContent += "\\";
|
|
53
|
+
} else if (level > 0) {
|
|
54
|
+
if (char === "/" && nextChar === "/") {
|
|
55
|
+
// Skip one-line comments
|
|
56
|
+
let start = i;
|
|
57
|
+
i = value.indexOf("\n", i);
|
|
58
|
+
textContent += value.substring(start, i + 1);
|
|
59
|
+
continue;
|
|
60
|
+
} else if (char === "/" && nextChar === "*") {
|
|
61
|
+
// Skip block comments
|
|
62
|
+
let start = i;
|
|
63
|
+
i = value.indexOf("*/", i) + 1;
|
|
64
|
+
textContent += value.substring(start, i + 1);
|
|
65
|
+
continue;
|
|
66
|
+
} else if (char === '"' || char === "'") {
|
|
67
|
+
// Skip string contents
|
|
68
|
+
let start = i;
|
|
69
|
+
i = endOfString(char, value, i);
|
|
70
|
+
textContent += value.substring(start, i + 1);
|
|
71
|
+
continue;
|
|
72
|
+
} else if (char === "`") {
|
|
73
|
+
// Skip interpolated string contents
|
|
74
|
+
let start = i;
|
|
75
|
+
i = endOfTemplateString(value, i);
|
|
76
|
+
textContent += value.substring(start, i + 1);
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
textContent += value[i];
|
|
81
|
+
}
|
|
82
|
+
value = `\`${textContent}\``;
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
newValue: value,
|
|
86
|
+
spans,
|
|
87
|
+
offsets,
|
|
88
|
+
lengths,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -5,13 +5,18 @@ import replaceForVarNames from "./replaceForVarNames";
|
|
|
5
5
|
|
|
6
6
|
export default function stashRunWithOffsets(
|
|
7
7
|
fragment: Fragment,
|
|
8
|
-
|
|
8
|
+
functionStart: string,
|
|
9
|
+
value: string,
|
|
10
|
+
functionEnd: string,
|
|
9
11
|
spans: SourceSpan[],
|
|
10
12
|
offsets: number[],
|
|
11
13
|
lengths: number[],
|
|
12
14
|
status: BuildStatus,
|
|
13
15
|
): void {
|
|
16
|
+
let functionBody = functionStart + value + functionEnd;
|
|
17
|
+
|
|
14
18
|
for (let i = 0; i < offsets.length; i++) {
|
|
19
|
+
offsets[i] += functionStart.length;
|
|
15
20
|
const oldLength = functionBody.length;
|
|
16
21
|
functionBody =
|
|
17
22
|
functionBody.substring(0, offsets[i]) +
|
|
@@ -23,6 +28,7 @@ export default function stashRunWithOffsets(
|
|
|
23
28
|
offsets[j] += changedLength;
|
|
24
29
|
}
|
|
25
30
|
}
|
|
31
|
+
|
|
26
32
|
fragment.effects.push({
|
|
27
33
|
functionBody,
|
|
28
34
|
spans: spans,
|
|
@@ -105,7 +105,7 @@ function buildElementAttributes(node: ElementNode, status: BuildServerStatus) {
|
|
|
105
105
|
// TODO: Match braces, don't replace braces inside code
|
|
106
106
|
status.imports.add("t_attr");
|
|
107
107
|
attributes.push(
|
|
108
|
-
`${name}="${trimQuotes(value).replaceAll("
|
|
108
|
+
`${name}="${trimQuotes(value).replaceAll("{", "${t_attr(").replaceAll("}", ")}")}"`,
|
|
109
109
|
);
|
|
110
110
|
} else if (name === "class" && node.scopeStyles) {
|
|
111
111
|
value = value
|