@volter/editor-react 0.5.66
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/LICENSE +661 -0
- package/NOTICE +4 -0
- package/contributions/component-contracts.service.ts +18 -0
- package/dist-node/component-prop-worker.mjs +335 -0
- package/dist-node/serving.mjs +5842 -0
- package/package.json +51 -0
- package/serving/component-prop-worker.ts +72 -0
- package/serving/index.ts +21 -0
- package/serving/ui-oid-plugin.ts +1844 -0
- package/src/source/component-prop-types.ts +517 -0
- package/src/source/ensure-import.ts +132 -0
- package/src/source/oid-transform.ts +1667 -0
- package/src/source/plan-csf-story.ts +196 -0
- package/src/source/plan-extract-component.ts +421 -0
- package/src/source/plan-fork-component.ts +621 -0
- package/src/source/plan-named-style.ts +95 -0
- package/src/source/plan-source-edit.ts +353 -0
- package/src/source/r3f-contract-resolver.ts +327 -0
- package/src/source/r3f-diagnostic-index.ts +165 -0
- package/src/source/r3f-environment-binding.ts +106 -0
- package/src/source/r3f-joint-binding.ts +228 -0
- package/src/source/r3f-lod-binding.ts +81 -0
- package/src/source/r3f-particle-binding.ts +213 -0
- package/src/source/r3f-physics-binding.ts +454 -0
- package/src/source/r3f-project-contracts.ts +444 -0
- package/src/source/relative-import-specifier.ts +34 -0
- package/src/source/reparent-guard.ts +531 -0
- package/src/source/source-edit-request.ts +22 -0
- package/src/source/syntactic-prop-specs.ts +296 -0
- package/src/source/ts-ast.ts +171 -0
- package/src/source/utility-class-support.ts +169 -0
- package/src/source/write-component-default.ts +133 -0
- package/src/source/writer.ts +1982 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Surgical native TypeScript edit for “Apply to Component”. The selected
|
|
3
|
+
* instance supplies a literal value; the OID index supplies the component
|
|
4
|
+
* declaration name. Only an existing literal destructuring default is
|
|
5
|
+
* writable — computed defaults are source-owned logic and are refused.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import ts from 'typescript';
|
|
9
|
+
import { parseAuthoringTsx } from './ts-ast';
|
|
10
|
+
|
|
11
|
+
export interface ComponentDefaultEditResult {
|
|
12
|
+
readonly code: string;
|
|
13
|
+
readonly changed: boolean;
|
|
14
|
+
readonly dynamic: boolean;
|
|
15
|
+
readonly error?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function componentParameter(node: ts.Node, componentName: string): ts.ParameterDeclaration | null {
|
|
19
|
+
if (ts.isFunctionDeclaration(node) && node.name?.text === componentName) {
|
|
20
|
+
return node.parameters[0] ?? null;
|
|
21
|
+
}
|
|
22
|
+
if (
|
|
23
|
+
ts.isVariableDeclaration(node) &&
|
|
24
|
+
ts.isIdentifier(node.name) &&
|
|
25
|
+
node.name.text === componentName &&
|
|
26
|
+
node.initializer &&
|
|
27
|
+
(ts.isArrowFunction(node.initializer) || ts.isFunctionExpression(node.initializer))
|
|
28
|
+
) {
|
|
29
|
+
return node.initializer.parameters[0] ?? null;
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function findComponentParameter(
|
|
35
|
+
node: ts.Node,
|
|
36
|
+
componentName: string,
|
|
37
|
+
): ts.ParameterDeclaration | null {
|
|
38
|
+
const direct = componentParameter(node, componentName);
|
|
39
|
+
if (direct) return direct;
|
|
40
|
+
let nested: ts.ParameterDeclaration | null = null;
|
|
41
|
+
node.forEachChild((child) => {
|
|
42
|
+
if (!nested) nested = findComponentParameter(child, componentName);
|
|
43
|
+
});
|
|
44
|
+
return nested;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function stringReplacement(initializer: ts.Expression, value: string): string | null {
|
|
48
|
+
if (!ts.isStringLiteral(initializer) && !ts.isNoSubstitutionTemplateLiteral(initializer)) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
const quote = initializer.getText()[0];
|
|
52
|
+
if (quote === "'") {
|
|
53
|
+
return `'${value.replaceAll('\\', '\\\\').replaceAll("'", "\\'").replaceAll('\n', '\\n')}'`;
|
|
54
|
+
}
|
|
55
|
+
if (quote === '`') {
|
|
56
|
+
return `\`${value
|
|
57
|
+
.replaceAll('\\', '\\\\')
|
|
58
|
+
.replaceAll('`', '\\`')
|
|
59
|
+
.replaceAll('${', '\\${')
|
|
60
|
+
.replaceAll('\n', '\\n')}\``;
|
|
61
|
+
}
|
|
62
|
+
return JSON.stringify(value);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function literalReplacement(initializer: ts.Expression, value: string): string | null {
|
|
66
|
+
const stringLiteral = stringReplacement(initializer, value);
|
|
67
|
+
if (stringLiteral !== null) return stringLiteral;
|
|
68
|
+
if (
|
|
69
|
+
ts.isNumericLiteral(initializer) ||
|
|
70
|
+
(ts.isPrefixUnaryExpression(initializer) && ts.isNumericLiteral(initializer.operand))
|
|
71
|
+
) {
|
|
72
|
+
return Number.isFinite(Number(value)) ? value : null;
|
|
73
|
+
}
|
|
74
|
+
if (
|
|
75
|
+
initializer.kind === ts.SyntaxKind.TrueKeyword ||
|
|
76
|
+
initializer.kind === ts.SyntaxKind.FalseKeyword
|
|
77
|
+
) {
|
|
78
|
+
return value === 'true' || value === 'false' ? value : null;
|
|
79
|
+
}
|
|
80
|
+
if (ts.isArrayLiteralExpression(initializer)) {
|
|
81
|
+
const trimmed = value.trim();
|
|
82
|
+
return /^\[\s*-?(?:\d+(?:\.\d+)?|\.\d+)(?:\s*,\s*-?(?:\d+(?:\.\d+)?|\.\d+))*\s*\]$/.test(
|
|
83
|
+
trimmed,
|
|
84
|
+
)
|
|
85
|
+
? trimmed
|
|
86
|
+
: null;
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function writeComponentDefault(
|
|
92
|
+
code: string,
|
|
93
|
+
fileName: string,
|
|
94
|
+
componentName: string,
|
|
95
|
+
prop: string,
|
|
96
|
+
value: string,
|
|
97
|
+
): ComponentDefaultEditResult {
|
|
98
|
+
const source = parseAuthoringTsx(fileName, code);
|
|
99
|
+
const parameter = findComponentParameter(source, componentName);
|
|
100
|
+
if (!parameter || !ts.isObjectBindingPattern(parameter.name)) {
|
|
101
|
+
return {
|
|
102
|
+
code,
|
|
103
|
+
changed: false,
|
|
104
|
+
dynamic: true,
|
|
105
|
+
error: `${componentName} does not declare literal destructured defaults.`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const binding = parameter.name.elements.find((element) => {
|
|
109
|
+
const authoredName = element.propertyName ?? element.name;
|
|
110
|
+
return ts.isIdentifier(authoredName) && authoredName.text === prop;
|
|
111
|
+
});
|
|
112
|
+
if (!binding?.initializer) {
|
|
113
|
+
return {
|
|
114
|
+
code,
|
|
115
|
+
changed: false,
|
|
116
|
+
dynamic: true,
|
|
117
|
+
error: `${componentName}.${prop} has no declared default initializer.`,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
const replacement = literalReplacement(binding.initializer, value);
|
|
121
|
+
if (replacement === null) {
|
|
122
|
+
return {
|
|
123
|
+
code,
|
|
124
|
+
changed: false,
|
|
125
|
+
dynamic: true,
|
|
126
|
+
error: `${componentName}.${prop} uses a computed or incompatible default.`,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
const start = binding.initializer.getStart(source);
|
|
130
|
+
const end = binding.initializer.getEnd();
|
|
131
|
+
const next = code.slice(0, start) + replacement + code.slice(end);
|
|
132
|
+
return { code: next, changed: next !== code, dynamic: false };
|
|
133
|
+
}
|