@thyn/core 0.0.351 → 0.0.352

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.
@@ -168,13 +168,13 @@ export function splitScript(script) {
168
168
  let inString = false;
169
169
  let stringChar = "";
170
170
  let inMultiLineComment = false;
171
+ let escaped = false;
171
172
  // Helper function to check if import is complete without semicolon
172
- function isImportComplete(line, braceCount, inString) {
173
+ function isImportComplete(lineIndex, braceCount, inString) {
173
174
  // If we have balanced braces and not in a string, check if next non-empty line starts a new statement
174
175
  if (braceCount === 0 && !inString) {
175
176
  // Look ahead to see if next line starts a new statement/declaration
176
- const nextLineIndex = lines.indexOf(line) + 1;
177
- for (let i = nextLineIndex; i < lines.length; i++) {
177
+ for (let i = lineIndex + 1; i < lines.length; i++) {
178
178
  const nextLine = lines[i].trim();
179
179
  if (!nextLine || nextLine.startsWith("//") || nextLine.startsWith("/*")) {
180
180
  continue; // Skip empty lines and comments
@@ -188,6 +188,7 @@ export function splitScript(script) {
188
188
  }
189
189
  return false;
190
190
  }
191
+ // Process each line, maintaining string/comment state
191
192
  for (let i = 0; i < lines.length; i++) {
192
193
  const line = lines[i];
193
194
  const trimmed = line.trim();
@@ -199,13 +200,34 @@ export function splitScript(script) {
199
200
  else {
200
201
  body.push(line);
201
202
  }
202
- if (line.includes("*/")) {
203
- inMultiLineComment = false;
203
+ // Check for end of multi-line comment, tracking strings
204
+ for (let j = 0; j < line.length; j++) {
205
+ const char = line[j];
206
+ if (escaped) {
207
+ escaped = false;
208
+ continue;
209
+ }
210
+ if (char === '\\' && inString) {
211
+ escaped = true;
212
+ continue;
213
+ }
214
+ if (!inString && (char === '"' || char === "'" || char === '`')) {
215
+ inString = true;
216
+ stringChar = char;
217
+ }
218
+ else if (inString && char === stringChar) {
219
+ inString = false;
220
+ stringChar = "";
221
+ }
222
+ else if (!inString && char === '*' && line[j + 1] === '/') {
223
+ inMultiLineComment = false;
224
+ break;
225
+ }
204
226
  }
205
227
  continue;
206
228
  }
207
- // Check for start of multi-line comment
208
- if (line.includes("/*") && !inString) {
229
+ // Check for start of multi-line comment (only if not in string)
230
+ if (!inString && line.includes("/*")) {
209
231
  inMultiLineComment = true;
210
232
  if (inImport) {
211
233
  currentImport.push(line);
@@ -213,15 +235,37 @@ export function splitScript(script) {
213
235
  else {
214
236
  body.push(line);
215
237
  }
216
- if (!line.includes("*/")) {
217
- continue;
238
+ // Check if comment ends on same line
239
+ for (let j = 0; j < line.length; j++) {
240
+ const char = line[j];
241
+ if (escaped) {
242
+ escaped = false;
243
+ continue;
244
+ }
245
+ if (char === '\\' && inString) {
246
+ escaped = true;
247
+ continue;
248
+ }
249
+ if (!inString && (char === '"' || char === "'" || char === '`')) {
250
+ inString = true;
251
+ stringChar = char;
252
+ }
253
+ else if (inString && char === stringChar) {
254
+ inString = false;
255
+ stringChar = "";
256
+ }
257
+ else if (!inString && char === '*' && line[j + 1] === '/') {
258
+ inMultiLineComment = false;
259
+ break;
260
+ }
218
261
  }
219
- else {
220
- inMultiLineComment = false;
262
+ if (!inMultiLineComment) {
263
+ // Comment ended on same line
264
+ continue;
221
265
  }
222
266
  }
223
- // Skip single-line comments when not in import
224
- if (trimmed.startsWith("//") && !inImport) {
267
+ // Skip single-line comments when not in import (only if not in string)
268
+ if (!inString && !inImport && trimmed.startsWith("//")) {
225
269
  body.push(line);
226
270
  continue;
227
271
  }
@@ -230,37 +274,46 @@ export function splitScript(script) {
230
274
  body.push(line);
231
275
  continue;
232
276
  }
233
- // Start of import statement
234
- if (!inImport && trimmed.startsWith("import")) {
277
+ // Process the line character by character to maintain string state
278
+ let lineBraceCount = 0;
279
+ let lineInString = inString;
280
+ let lineStringChar = stringChar;
281
+ let lineEscaped = false;
282
+ for (let j = 0; j < line.length; j++) {
283
+ const char = line[j];
284
+ if (lineEscaped) {
285
+ lineEscaped = false;
286
+ continue;
287
+ }
288
+ if (char === '\\' && lineInString) {
289
+ lineEscaped = true;
290
+ continue;
291
+ }
292
+ if (!lineInString && (char === '"' || char === "'" || char === '`')) {
293
+ lineInString = true;
294
+ lineStringChar = char;
295
+ }
296
+ else if (lineInString && char === lineStringChar) {
297
+ lineInString = false;
298
+ lineStringChar = "";
299
+ }
300
+ else if (!lineInString && char === '{') {
301
+ lineBraceCount++;
302
+ }
303
+ else if (!lineInString && char === '}') {
304
+ lineBraceCount--;
305
+ }
306
+ }
307
+ // Start of import statement (only if not inside a string)
308
+ if (!inImport && !inString && trimmed.startsWith("import")) {
235
309
  inImport = true;
236
310
  currentImport = [line];
237
- braceCount = 0;
238
- inString = false;
239
- // Count braces and track strings in the import line
240
- for (let j = 0; j < line.length; j++) {
241
- const char = line[j];
242
- if (inString) {
243
- if (char === stringChar && line[j - 1] !== "\\") {
244
- inString = false;
245
- stringChar = "";
246
- }
247
- }
248
- else {
249
- if (char === '"' || char === "'" || char === "`") {
250
- inString = true;
251
- stringChar = char;
252
- }
253
- else if (char === "{") {
254
- braceCount++;
255
- }
256
- else if (char === "}") {
257
- braceCount--;
258
- }
259
- }
260
- }
261
- // Check if import is complete
311
+ braceCount = lineBraceCount;
312
+ inString = lineInString;
313
+ stringChar = lineStringChar;
314
+ // Check if import is complete on this line
262
315
  if ((trimmed.endsWith(";") ||
263
- isImportComplete(trimmed, braceCount, inString)) &&
316
+ isImportComplete(i, braceCount, inString)) &&
264
317
  braceCount === 0 && !inString) {
265
318
  imports.push(currentImport.join("\n"));
266
319
  currentImport = [];
@@ -269,31 +322,12 @@ export function splitScript(script) {
269
322
  } // Continue import statement
270
323
  else if (inImport) {
271
324
  currentImport.push(line);
272
- // Count braces and track strings in the current line
273
- for (let j = 0; j < line.length; j++) {
274
- const char = line[j];
275
- if (inString) {
276
- if (char === stringChar && line[j - 1] !== "\\") {
277
- inString = false;
278
- stringChar = "";
279
- }
280
- }
281
- else {
282
- if (char === '"' || char === "'" || char === "`") {
283
- inString = true;
284
- stringChar = char;
285
- }
286
- else if (char === "{") {
287
- braceCount++;
288
- }
289
- else if (char === "}") {
290
- braceCount--;
291
- }
292
- }
293
- }
325
+ braceCount += lineBraceCount;
326
+ inString = lineInString;
327
+ stringChar = lineStringChar;
294
328
  // Check if import is complete
295
329
  if ((trimmed.endsWith(";") ||
296
- isImportComplete(trimmed, braceCount, inString)) &&
330
+ isImportComplete(i, braceCount, inString)) &&
297
331
  braceCount === 0 && !inString) {
298
332
  imports.push(currentImport.join("\n"));
299
333
  currentImport = [];
@@ -302,11 +336,21 @@ export function splitScript(script) {
302
336
  } // Regular body content
303
337
  else {
304
338
  body.push(line);
339
+ // Update global string state
340
+ inString = lineInString;
341
+ stringChar = lineStringChar;
305
342
  }
306
343
  }
307
- // Handle unterminated import (likely malformed)
344
+ // Handle unterminated import (likely malformed or still in string)
308
345
  if (currentImport.length > 0) {
309
- imports.push(currentImport.join("\n"));
346
+ if (inImport && !inString) {
347
+ // Import seems complete but wasn't captured properly
348
+ imports.push(currentImport.join("\n"));
349
+ }
350
+ else {
351
+ // Still in string or incomplete, treat as body
352
+ body.push(...currentImport);
353
+ }
310
354
  }
311
355
  return {
312
356
  imports: imports.filter((imp) => imp.trim()),
@@ -8,7 +8,7 @@
8
8
  "name": "thyn-app",
9
9
  "version": "0.0.0",
10
10
  "devDependencies": {
11
- "@thyn/core": "^0.0.350",
11
+ "@thyn/core": "^0.0.351",
12
12
  "vite": "^6.3.5"
13
13
  }
14
14
  },
@@ -742,9 +742,9 @@
742
742
  ]
743
743
  },
744
744
  "node_modules/@thyn/core": {
745
- "version": "0.0.350",
746
- "resolved": "https://registry.npmjs.org/@thyn/core/-/core-0.0.350.tgz",
747
- "integrity": "sha512-SZTB1jjjsXEkZg1BlH7s4Lhe8kWN/74ufNlNMnI86x+9tiyc0yvMLh+GFhe7VkIS//NrEwdgjR3ktpJlGeMEfA==",
745
+ "version": "0.0.351",
746
+ "resolved": "https://registry.npmjs.org/@thyn/core/-/core-0.0.351.tgz",
747
+ "integrity": "sha512-67M0/0wrdz1mNIMZ9V7Nq1jEO1C1iLdoG9rnjFcDGlDQRFM/HlNizvVWOaq2RfapVpgtRqIsKAA5iBloLE2GIA==",
748
748
  "dev": true,
749
749
  "license": "MIT",
750
750
  "dependencies": {
package/docs/package.json CHANGED
@@ -9,7 +9,7 @@
9
9
  "preview": "vite preview"
10
10
  },
11
11
  "devDependencies": {
12
- "@thyn/core": "^0.0.350",
12
+ "@thyn/core": "^0.0.351",
13
13
  "vite": "^6.3.5"
14
14
  }
15
15
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thyn/core",
3
- "version": "0.0.351",
3
+ "version": "0.0.352",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "exports": {
@@ -186,20 +186,20 @@ export function splitScript(script: string) {
186
186
  const lines = script.split("\n");
187
187
  const imports = [];
188
188
  const body = [];
189
- let currentImport = [];
189
+ let currentImport: string[] = [];
190
190
  let inImport = false;
191
191
  let braceCount = 0;
192
192
  let inString = false;
193
193
  let stringChar = "";
194
194
  let inMultiLineComment = false;
195
+ let escaped = false;
195
196
 
196
197
  // Helper function to check if import is complete without semicolon
197
- function isImportComplete(line, braceCount, inString) {
198
+ function isImportComplete(lineIndex: number, braceCount: number, inString: boolean): boolean {
198
199
  // If we have balanced braces and not in a string, check if next non-empty line starts a new statement
199
200
  if (braceCount === 0 && !inString) {
200
201
  // Look ahead to see if next line starts a new statement/declaration
201
- const nextLineIndex = lines.indexOf(line) + 1;
202
- for (let i = nextLineIndex; i < lines.length; i++) {
202
+ for (let i = lineIndex + 1; i < lines.length; i++) {
203
203
  const nextLine = lines[i].trim();
204
204
  if (
205
205
  !nextLine || nextLine.startsWith("//") || nextLine.startsWith("/*")
@@ -216,6 +216,7 @@ export function splitScript(script: string) {
216
216
  return false;
217
217
  }
218
218
 
219
+ // Process each line, maintaining string/comment state
219
220
  for (let i = 0; i < lines.length; i++) {
220
221
  const line = lines[i];
221
222
  const trimmed = line.trim();
@@ -228,14 +229,33 @@ export function splitScript(script: string) {
228
229
  body.push(line);
229
230
  }
230
231
 
231
- if (line.includes("*/")) {
232
- inMultiLineComment = false;
232
+ // Check for end of multi-line comment, tracking strings
233
+ for (let j = 0; j < line.length; j++) {
234
+ const char = line[j];
235
+ if (escaped) {
236
+ escaped = false;
237
+ continue;
238
+ }
239
+ if (char === '\\' && inString) {
240
+ escaped = true;
241
+ continue;
242
+ }
243
+ if (!inString && (char === '"' || char === "'" || char === '`')) {
244
+ inString = true;
245
+ stringChar = char;
246
+ } else if (inString && char === stringChar) {
247
+ inString = false;
248
+ stringChar = "";
249
+ } else if (!inString && char === '*' && line[j + 1] === '/') {
250
+ inMultiLineComment = false;
251
+ break;
252
+ }
233
253
  }
234
254
  continue;
235
255
  }
236
256
 
237
- // Check for start of multi-line comment
238
- if (line.includes("/*") && !inString) {
257
+ // Check for start of multi-line comment (only if not in string)
258
+ if (!inString && line.includes("/*")) {
239
259
  inMultiLineComment = true;
240
260
  if (inImport) {
241
261
  currentImport.push(line);
@@ -243,15 +263,37 @@ export function splitScript(script: string) {
243
263
  body.push(line);
244
264
  }
245
265
 
246
- if (!line.includes("*/")) {
266
+ // Check if comment ends on same line
267
+ for (let j = 0; j < line.length; j++) {
268
+ const char = line[j];
269
+ if (escaped) {
270
+ escaped = false;
271
+ continue;
272
+ }
273
+ if (char === '\\' && inString) {
274
+ escaped = true;
275
+ continue;
276
+ }
277
+ if (!inString && (char === '"' || char === "'" || char === '`')) {
278
+ inString = true;
279
+ stringChar = char;
280
+ } else if (inString && char === stringChar) {
281
+ inString = false;
282
+ stringChar = "";
283
+ } else if (!inString && char === '*' && line[j + 1] === '/') {
284
+ inMultiLineComment = false;
285
+ break;
286
+ }
287
+ }
288
+
289
+ if (!inMultiLineComment) {
290
+ // Comment ended on same line
247
291
  continue;
248
- } else {
249
- inMultiLineComment = false;
250
292
  }
251
293
  }
252
294
 
253
- // Skip single-line comments when not in import
254
- if (trimmed.startsWith("//") && !inImport) {
295
+ // Skip single-line comments when not in import (only if not in string)
296
+ if (!inString && !inImport && trimmed.startsWith("//")) {
255
297
  body.push(line);
256
298
  continue;
257
299
  }
@@ -262,38 +304,50 @@ export function splitScript(script: string) {
262
304
  continue;
263
305
  }
264
306
 
265
- // Start of import statement
266
- if (!inImport && trimmed.startsWith("import")) {
267
- inImport = true;
268
- currentImport = [line];
269
- braceCount = 0;
270
- inString = false;
307
+ // Process the line character by character to maintain string state
308
+ let lineBraceCount = 0;
309
+ let lineInString = inString;
310
+ let lineStringChar = stringChar;
311
+ let lineEscaped = false;
271
312
 
272
- // Count braces and track strings in the import line
273
- for (let j = 0; j < line.length; j++) {
274
- const char = line[j];
313
+ for (let j = 0; j < line.length; j++) {
314
+ const char = line[j];
275
315
 
276
- if (inString) {
277
- if (char === stringChar && line[j - 1] !== "\\") {
278
- inString = false;
279
- stringChar = "";
280
- }
281
- } else {
282
- if (char === '"' || char === "'" || char === "`") {
283
- inString = true;
284
- stringChar = char;
285
- } else if (char === "{") {
286
- braceCount++;
287
- } else if (char === "}") {
288
- braceCount--;
289
- }
290
- }
316
+ if (lineEscaped) {
317
+ lineEscaped = false;
318
+ continue;
291
319
  }
292
320
 
293
- // Check if import is complete
321
+ if (char === '\\' && lineInString) {
322
+ lineEscaped = true;
323
+ continue;
324
+ }
325
+
326
+ if (!lineInString && (char === '"' || char === "'" || char === '`')) {
327
+ lineInString = true;
328
+ lineStringChar = char;
329
+ } else if (lineInString && char === lineStringChar) {
330
+ lineInString = false;
331
+ lineStringChar = "";
332
+ } else if (!lineInString && char === '{') {
333
+ lineBraceCount++;
334
+ } else if (!lineInString && char === '}') {
335
+ lineBraceCount--;
336
+ }
337
+ }
338
+
339
+ // Start of import statement (only if not inside a string)
340
+ if (!inImport && !inString && trimmed.startsWith("import")) {
341
+ inImport = true;
342
+ currentImport = [line];
343
+ braceCount = lineBraceCount;
344
+ inString = lineInString;
345
+ stringChar = lineStringChar;
346
+
347
+ // Check if import is complete on this line
294
348
  if (
295
349
  (trimmed.endsWith(";") ||
296
- isImportComplete(trimmed, braceCount, inString)) &&
350
+ isImportComplete(i, braceCount, inString)) &&
297
351
  braceCount === 0 && !inString
298
352
  ) {
299
353
  imports.push(currentImport.join("\n"));
@@ -303,32 +357,14 @@ export function splitScript(script: string) {
303
357
  } // Continue import statement
304
358
  else if (inImport) {
305
359
  currentImport.push(line);
306
-
307
- // Count braces and track strings in the current line
308
- for (let j = 0; j < line.length; j++) {
309
- const char = line[j];
310
-
311
- if (inString) {
312
- if (char === stringChar && line[j - 1] !== "\\") {
313
- inString = false;
314
- stringChar = "";
315
- }
316
- } else {
317
- if (char === '"' || char === "'" || char === "`") {
318
- inString = true;
319
- stringChar = char;
320
- } else if (char === "{") {
321
- braceCount++;
322
- } else if (char === "}") {
323
- braceCount--;
324
- }
325
- }
326
- }
360
+ braceCount += lineBraceCount;
361
+ inString = lineInString;
362
+ stringChar = lineStringChar;
327
363
 
328
364
  // Check if import is complete
329
365
  if (
330
366
  (trimmed.endsWith(";") ||
331
- isImportComplete(trimmed, braceCount, inString)) &&
367
+ isImportComplete(i, braceCount, inString)) &&
332
368
  braceCount === 0 && !inString
333
369
  ) {
334
370
  imports.push(currentImport.join("\n"));
@@ -338,12 +374,21 @@ export function splitScript(script: string) {
338
374
  } // Regular body content
339
375
  else {
340
376
  body.push(line);
377
+ // Update global string state
378
+ inString = lineInString;
379
+ stringChar = lineStringChar;
341
380
  }
342
381
  }
343
382
 
344
- // Handle unterminated import (likely malformed)
383
+ // Handle unterminated import (likely malformed or still in string)
345
384
  if (currentImport.length > 0) {
346
- imports.push(currentImport.join("\n"));
385
+ if (inImport && !inString) {
386
+ // Import seems complete but wasn't captured properly
387
+ imports.push(currentImport.join("\n"));
388
+ } else {
389
+ // Still in string or incomplete, treat as body
390
+ body.push(...currentImport);
391
+ }
347
392
  }
348
393
 
349
394
  return {
@@ -0,0 +1,15 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import ImportInString from "./ImportInString.thyn";
3
+
4
+ describe("ImportInString component", () => {
5
+ it("should not treat imports inside string literals as real imports", () => {
6
+ const root = ImportInString();
7
+ expect(root.textContent).toBe("42");
8
+ });
9
+
10
+ it("should include the import statements in the codeSnippet", () => {
11
+ const root = ImportInString();
12
+ // The codeSnippet should be available (this tests the script was parsed correctly)
13
+ expect(root).toBeTruthy();
14
+ });
15
+ });
@@ -0,0 +1,15 @@
1
+ <script>
2
+ const codeSnippet = `// main.js
3
+ import { mount } from '@thyn/core';
4
+ import App from './App.thyn';
5
+
6
+ mount(App, document.body);`;
7
+
8
+ const value = 42;
9
+ </script>
10
+
11
+ <div>{{ value }}</div>
12
+
13
+ <style>
14
+ div { color: red; }
15
+ </style>