@sprlab/wccompiler 0.12.1 → 0.14.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.
@@ -1,160 +1,160 @@
1
- /**
2
- * Import resolver for .wcc component imports.
3
- *
4
- * Extracts and validates `.wcc` import statements from the script block,
5
- * producing a structured representation of named default imports and
6
- * side-effect imports. Rejects invalid import forms (namespace, named exports).
7
- */
8
-
9
- // ── Types ────────────────────────────────────────────────────────────
10
-
11
- /**
12
- * @typedef {Object} WccNamedImport
13
- * @property {string} identifier — The import name (e.g., 'WccBadge', 'MyButton')
14
- * @property {string} sourcePath — Original .wcc path (e.g., './wcc-badge.wcc')
15
- * @property {string} compiledPath — Rewritten .js path (e.g., './wcc-badge.js')
16
- */
17
-
18
- /**
19
- * @typedef {Object} WccSideEffectImport
20
- * @property {string} sourcePath — Original .wcc path (e.g., './child.wcc')
21
- * @property {string} compiledPath — Rewritten .js path (e.g., './child.js')
22
- */
23
-
24
- /**
25
- * @typedef {Object} WccImportResult
26
- * @property {WccNamedImport[]} named — Named default imports
27
- * @property {WccSideEffectImport[]} sideEffect — Side-effect imports
28
- * @property {string} strippedSource — Script source with .wcc imports removed
29
- */
30
-
31
- // ── Regex patterns ───────────────────────────────────────────────────
32
-
33
- /**
34
- * Matches any import statement that references a .wcc file.
35
- * Captures the full import line for removal and classification.
36
- *
37
- * Groups:
38
- * - Full match: the entire import statement line
39
- *
40
- * We use individual patterns below for classification.
41
- */
42
- const WCC_IMPORT_LINE_RE = /^[ \t]*import\s+.*?['"]([^'"]+\.wcc)['"]\s*;?[ \t]*$/gm;
43
-
44
- /**
45
- * Named default import: import Identifier from './path.wcc'
46
- */
47
- const NAMED_DEFAULT_RE = /^[ \t]*import\s+([$\w]+)\s+from\s+['"]([^'"]+\.wcc)['"]\s*;?[ \t]*$/;
48
-
49
- /**
50
- * Side-effect import: import './path.wcc'
51
- */
52
- const SIDE_EFFECT_RE = /^[ \t]*import\s+['"]([^'"]+\.wcc)['"]\s*;?[ \t]*$/;
53
-
54
- /**
55
- * Namespace import: import * as Foo from './path.wcc'
56
- */
57
- const NAMESPACE_RE = /^[ \t]*import\s+\*\s+as\s+\w+\s+from\s+['"][^'"]+\.wcc['"]\s*;?[ \t]*$/;
58
-
59
- /**
60
- * Named exports import: import { Foo } from './path.wcc'
61
- * Also matches: import { Foo, Bar } from './path.wcc'
62
- * Also matches: import { Foo as Bar } from './path.wcc'
63
- */
64
- const NAMED_EXPORTS_RE = /^[ \t]*import\s+\{[^}]*\}\s+from\s+['"][^'"]+\.wcc['"]\s*;?[ \t]*$/;
65
-
66
- // ── Helpers ──────────────────────────────────────────────────────────
67
-
68
- /**
69
- * Rewrite a .wcc path to .js by replacing the extension.
70
- * Preserves all relative path segments.
71
- *
72
- * @param {string} wccPath — e.g., '../shared/wcc-button.wcc'
73
- * @returns {string} — e.g., '../shared/wcc-button.js'
74
- */
75
- function rewriteExtension(wccPath) {
76
- return wccPath.replace(/\.wcc$/, '.js');
77
- }
78
-
79
- // ── Main export ──────────────────────────────────────────────────────
80
-
81
- /**
82
- * Extract all .wcc imports from a script source string.
83
- * Validates import forms and rejects invalid patterns.
84
- *
85
- * @param {string} source — Script block content
86
- * @param {string} fileName — Source file name for error messages
87
- * @returns {WccImportResult}
88
- * @throws {Error} with code 'INVALID_WCC_IMPORT' for namespace/named exports
89
- */
90
- export function extractWccImports(source, fileName) {
91
- /** @type {WccNamedImport[]} */
92
- const named = [];
93
- /** @type {WccSideEffectImport[]} */
94
- const sideEffect = [];
95
-
96
- // Collect all .wcc import lines for processing
97
- const lines = source.split('\n');
98
- /** @type {Set<number>} */
99
- const linesToRemove = new Set();
100
-
101
- for (let i = 0; i < lines.length; i++) {
102
- const line = lines[i];
103
-
104
- // Check if this line contains a .wcc import
105
- if (!line.match(/import\s.*\.wcc['"]/) && !line.match(/import\s+['"][^'"]+\.wcc['"]/)) {
106
- continue;
107
- }
108
-
109
- // Check for invalid forms first
110
- if (NAMESPACE_RE.test(line)) {
111
- const error = new Error(
112
- `Invalid import form in '${fileName}': .wcc files only support default imports (import Foo from './foo.wcc') or side-effect imports (import './foo.wcc')`
113
- );
114
- /** @ts-expect-error — custom error code for programmatic handling */
115
- error.code = 'INVALID_WCC_IMPORT';
116
- throw error;
117
- }
118
-
119
- if (NAMED_EXPORTS_RE.test(line)) {
120
- const error = new Error(
121
- `Invalid import form in '${fileName}': .wcc files only support default imports (import Foo from './foo.wcc') or side-effect imports (import './foo.wcc')`
122
- );
123
- /** @ts-expect-error — custom error code for programmatic handling */
124
- error.code = 'INVALID_WCC_IMPORT';
125
- throw error;
126
- }
127
-
128
- // Check for named default import
129
- const namedMatch = line.match(NAMED_DEFAULT_RE);
130
- if (namedMatch) {
131
- const identifier = namedMatch[1];
132
- const sourcePath = namedMatch[2];
133
- named.push({
134
- identifier,
135
- sourcePath,
136
- compiledPath: rewriteExtension(sourcePath),
137
- });
138
- linesToRemove.add(i);
139
- continue;
140
- }
141
-
142
- // Check for side-effect import
143
- const sideEffectMatch = line.match(SIDE_EFFECT_RE);
144
- if (sideEffectMatch) {
145
- const sourcePath = sideEffectMatch[1];
146
- sideEffect.push({
147
- sourcePath,
148
- compiledPath: rewriteExtension(sourcePath),
149
- });
150
- linesToRemove.add(i);
151
- continue;
152
- }
153
- }
154
-
155
- // Build stripped source by removing .wcc import lines
156
- const strippedLines = lines.filter((_, i) => !linesToRemove.has(i));
157
- const strippedSource = strippedLines.join('\n');
158
-
159
- return { named, sideEffect, strippedSource };
160
- }
1
+ /**
2
+ * Import resolver for .wcc component imports.
3
+ *
4
+ * Extracts and validates `.wcc` import statements from the script block,
5
+ * producing a structured representation of named default imports and
6
+ * side-effect imports. Rejects invalid import forms (namespace, named exports).
7
+ */
8
+
9
+ // ── Types ────────────────────────────────────────────────────────────
10
+
11
+ /**
12
+ * @typedef {Object} WccNamedImport
13
+ * @property {string} identifier — The import name (e.g., 'WccBadge', 'MyButton')
14
+ * @property {string} sourcePath — Original .wcc path (e.g., './wcc-badge.wcc')
15
+ * @property {string} compiledPath — Rewritten .js path (e.g., './wcc-badge.js')
16
+ */
17
+
18
+ /**
19
+ * @typedef {Object} WccSideEffectImport
20
+ * @property {string} sourcePath — Original .wcc path (e.g., './child.wcc')
21
+ * @property {string} compiledPath — Rewritten .js path (e.g., './child.js')
22
+ */
23
+
24
+ /**
25
+ * @typedef {Object} WccImportResult
26
+ * @property {WccNamedImport[]} named — Named default imports
27
+ * @property {WccSideEffectImport[]} sideEffect — Side-effect imports
28
+ * @property {string} strippedSource — Script source with .wcc imports removed
29
+ */
30
+
31
+ // ── Regex patterns ───────────────────────────────────────────────────
32
+
33
+ /**
34
+ * Matches any import statement that references a .wcc file.
35
+ * Captures the full import line for removal and classification.
36
+ *
37
+ * Groups:
38
+ * - Full match: the entire import statement line
39
+ *
40
+ * We use individual patterns below for classification.
41
+ */
42
+ const WCC_IMPORT_LINE_RE = /^[ \t]*import\s+.*?['"]([^'"]+\.wcc)['"]\s*;?[ \t]*$/gm;
43
+
44
+ /**
45
+ * Named default import: import Identifier from './path.wcc'
46
+ */
47
+ const NAMED_DEFAULT_RE = /^[ \t]*import\s+([$\w]+)\s+from\s+['"]([^'"]+\.wcc)['"]\s*;?[ \t]*$/;
48
+
49
+ /**
50
+ * Side-effect import: import './path.wcc'
51
+ */
52
+ const SIDE_EFFECT_RE = /^[ \t]*import\s+['"]([^'"]+\.wcc)['"]\s*;?[ \t]*$/;
53
+
54
+ /**
55
+ * Namespace import: import * as Foo from './path.wcc'
56
+ */
57
+ const NAMESPACE_RE = /^[ \t]*import\s+\*\s+as\s+\w+\s+from\s+['"][^'"]+\.wcc['"]\s*;?[ \t]*$/;
58
+
59
+ /**
60
+ * Named exports import: import { Foo } from './path.wcc'
61
+ * Also matches: import { Foo, Bar } from './path.wcc'
62
+ * Also matches: import { Foo as Bar } from './path.wcc'
63
+ */
64
+ const NAMED_EXPORTS_RE = /^[ \t]*import\s+\{[^}]*\}\s+from\s+['"][^'"]+\.wcc['"]\s*;?[ \t]*$/;
65
+
66
+ // ── Helpers ──────────────────────────────────────────────────────────
67
+
68
+ /**
69
+ * Rewrite a .wcc path to .js by replacing the extension.
70
+ * Preserves all relative path segments.
71
+ *
72
+ * @param {string} wccPath — e.g., '../shared/wcc-button.wcc'
73
+ * @returns {string} — e.g., '../shared/wcc-button.js'
74
+ */
75
+ function rewriteExtension(wccPath) {
76
+ return wccPath.replace(/\.wcc$/, '.js');
77
+ }
78
+
79
+ // ── Main export ──────────────────────────────────────────────────────
80
+
81
+ /**
82
+ * Extract all .wcc imports from a script source string.
83
+ * Validates import forms and rejects invalid patterns.
84
+ *
85
+ * @param {string} source — Script block content
86
+ * @param {string} fileName — Source file name for error messages
87
+ * @returns {WccImportResult}
88
+ * @throws {Error} with code 'INVALID_WCC_IMPORT' for namespace/named exports
89
+ */
90
+ export function extractWccImports(source, fileName) {
91
+ /** @type {WccNamedImport[]} */
92
+ const named = [];
93
+ /** @type {WccSideEffectImport[]} */
94
+ const sideEffect = [];
95
+
96
+ // Collect all .wcc import lines for processing
97
+ const lines = source.split('\n');
98
+ /** @type {Set<number>} */
99
+ const linesToRemove = new Set();
100
+
101
+ for (let i = 0; i < lines.length; i++) {
102
+ const line = lines[i];
103
+
104
+ // Check if this line contains a .wcc import
105
+ if (!line.match(/import\s.*\.wcc['"]/) && !line.match(/import\s+['"][^'"]+\.wcc['"]/)) {
106
+ continue;
107
+ }
108
+
109
+ // Check for invalid forms first
110
+ if (NAMESPACE_RE.test(line)) {
111
+ const error = new Error(
112
+ `Invalid import form in '${fileName}': .wcc files only support default imports (import Foo from './foo.wcc') or side-effect imports (import './foo.wcc')`
113
+ );
114
+ /** @ts-expect-error — custom error code for programmatic handling */
115
+ error.code = 'INVALID_WCC_IMPORT';
116
+ throw error;
117
+ }
118
+
119
+ if (NAMED_EXPORTS_RE.test(line)) {
120
+ const error = new Error(
121
+ `Invalid import form in '${fileName}': .wcc files only support default imports (import Foo from './foo.wcc') or side-effect imports (import './foo.wcc')`
122
+ );
123
+ /** @ts-expect-error — custom error code for programmatic handling */
124
+ error.code = 'INVALID_WCC_IMPORT';
125
+ throw error;
126
+ }
127
+
128
+ // Check for named default import
129
+ const namedMatch = line.match(NAMED_DEFAULT_RE);
130
+ if (namedMatch) {
131
+ const identifier = namedMatch[1];
132
+ const sourcePath = namedMatch[2];
133
+ named.push({
134
+ identifier,
135
+ sourcePath,
136
+ compiledPath: rewriteExtension(sourcePath),
137
+ });
138
+ linesToRemove.add(i);
139
+ continue;
140
+ }
141
+
142
+ // Check for side-effect import
143
+ const sideEffectMatch = line.match(SIDE_EFFECT_RE);
144
+ if (sideEffectMatch) {
145
+ const sourcePath = sideEffectMatch[1];
146
+ sideEffect.push({
147
+ sourcePath,
148
+ compiledPath: rewriteExtension(sourcePath),
149
+ });
150
+ linesToRemove.add(i);
151
+ continue;
152
+ }
153
+ }
154
+
155
+ // Build stripped source by removing .wcc import lines
156
+ const strippedLines = lines.filter((_, i) => !linesToRemove.has(i));
157
+ const strippedSource = strippedLines.join('\n');
158
+
159
+ return { named, sideEffect, strippedSource };
160
+ }