Skip to content

Commit e59e92b

Browse files
Merge pull request #1 from QwikDev/feat/upgrade
feat: v2 migration files
2 parents d351080 + 0003a26 commit e59e92b

24 files changed

Lines changed: 2642 additions & 432 deletions

.planning/STATE.md

Lines changed: 0 additions & 188 deletions
This file was deleted.

.planning/quick/11-set-up-ci-github-actions-workflow/11-SUMMARY.md

Lines changed: 0 additions & 70 deletions
This file was deleted.

migrations/v2/apply-transforms.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import MagicString from "magic-string";
2+
import { parseSync } from "oxc-parser";
3+
import { readFileSync, writeFileSync } from "node:fs";
4+
import type { SourceReplacement, TransformFn } from "./types.ts";
5+
6+
/**
7+
* Parse-once, fan-out orchestrator for AST-based source transforms.
8+
*
9+
* Algorithm:
10+
* 1. Early return if no transforms provided
11+
* 2. Read source from disk once
12+
* 3. Parse once with oxc-parser; share ParseResult across all transforms
13+
* 4. Fan out: collect SourceReplacement[] from each transform into a flat list
14+
* 5. Early return if no replacements collected (file unchanged)
15+
* 6. Sort replacements descending by `start` (later offsets first) to prevent
16+
* MagicString offset corruption when applying earlier edits
17+
* 7. Apply all replacements via a single MagicString instance
18+
* 8. Write back to disk only if content changed
19+
*
20+
* @param filePath - Absolute path to the file to transform
21+
* @param transforms - Array of transform functions to apply
22+
*/
23+
export function applyTransforms(filePath: string, transforms: TransformFn[]): void {
24+
// Step 1: Early return for empty transform list
25+
if (transforms.length === 0) return;
26+
27+
// Step 2: Read source once
28+
const source = readFileSync(filePath, "utf-8");
29+
30+
// Step 3: Parse once
31+
const parseResult = parseSync(filePath, source, { sourceType: "module" });
32+
33+
// Step 4: Fan out — collect all replacements
34+
const allReplacements: SourceReplacement[] = [];
35+
for (const transform of transforms) {
36+
const replacements = transform(filePath, source, parseResult);
37+
allReplacements.push(...replacements);
38+
}
39+
40+
// Step 5: Early return if nothing to replace
41+
if (allReplacements.length === 0) return;
42+
43+
// Step 6: Sort descending by start so later offsets are applied first
44+
allReplacements.sort((a, b) => b.start - a.start);
45+
46+
// Step 6b: Detect overlapping replacements before applying (magic-string does not
47+
// always throw a useful error; we surface a descriptive one instead).
48+
// After descending sort, replacement[i].start >= replacement[i+1].start.
49+
// A collision occurs when replacement[i+1].end > replacement[i].start.
50+
for (let i = 0; i < allReplacements.length - 1; i++) {
51+
const curr = allReplacements[i]!;
52+
const next = allReplacements[i + 1]!;
53+
if (next.end > curr.start) {
54+
throw new Error(
55+
`applyTransforms: overlapping replacements detected in "${filePath}". ` +
56+
`Replacement at [${next.start}, ${next.end}) overlaps with [${curr.start}, ${curr.end}). ` +
57+
`Each transform must produce non-overlapping SourceReplacement ranges.`,
58+
);
59+
}
60+
}
61+
62+
// Step 7: Apply via single MagicString instance
63+
const ms = new MagicString(source);
64+
for (const { start, end, replacement } of allReplacements) {
65+
ms.overwrite(start, end, replacement);
66+
}
67+
68+
// Step 8: Write back only when content changed
69+
if (ms.hasChanged()) {
70+
writeFileSync(filePath, ms.toString(), "utf-8");
71+
}
72+
}

migrations/v2/binary-extensions.ts

Lines changed: 11 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { extname } from "node:path";
22

33
/**
4-
* Set of known binary file extensions (lowercased, including the dot).
5-
* Based on the sindresorhus/binary-extensions list.
4+
* Pruned set of binary file extensions relevant to Qwik projects (lowercased, including the dot).
5+
* Contains ~50 essential entries covering images, fonts, archives, executables, audio, video, and
6+
* other common binary formats. Excludes niche formats unlikely to appear in a Qwik project.
67
*/
78
export const BINARY_EXTENSIONS: Set<string> = new Set([
89
// Images
@@ -16,42 +17,17 @@ export const BINARY_EXTENSIONS: Set<string> = new Set([
1617
".svg",
1718
".tiff",
1819
".tif",
19-
".psd",
20-
".ai",
21-
".eps",
22-
".raw",
23-
".cr2",
24-
".nef",
25-
".orf",
26-
".sr2",
2720
".avif",
2821
".heic",
2922
".heif",
30-
".jxl",
3123
".apng",
32-
".cur",
33-
".ani",
34-
".jfif",
35-
".jp2",
36-
".j2k",
37-
".jpf",
38-
".jpx",
39-
".jpm",
4024

41-
// Documents
42-
".pdf",
43-
".doc",
44-
".docx",
45-
".xls",
46-
".xlsx",
47-
".ppt",
48-
".pptx",
49-
".odt",
50-
".ods",
51-
".odp",
52-
".pages",
53-
".numbers",
54-
".key",
25+
// Fonts
26+
".woff",
27+
".woff2",
28+
".ttf",
29+
".eot",
30+
".otf",
5531

5632
// Archives
5733
".zip",
@@ -61,56 +37,16 @@ export const BINARY_EXTENSIONS: Set<string> = new Set([
6137
".7z",
6238
".bz2",
6339
".xz",
64-
".lz",
65-
".lzma",
66-
".z",
6740
".tgz",
68-
".tbz",
69-
".tbz2",
70-
".txz",
71-
".tlz",
72-
".cab",
73-
".deb",
74-
".rpm",
75-
".apk",
76-
".ipa",
77-
".crx",
78-
".iso",
79-
".img",
80-
".dmg",
81-
".pkg",
82-
".msi",
8341

8442
// Executables and binaries
8543
".exe",
8644
".dll",
8745
".so",
8846
".dylib",
89-
".lib",
9047
".a",
9148
".o",
92-
".obj",
93-
".pdb",
94-
".com",
95-
".bat",
96-
".cmd",
97-
".scr",
98-
".msc",
9949
".bin",
100-
".elf",
101-
".out",
102-
".app",
103-
104-
// Fonts
105-
".woff",
106-
".woff2",
107-
".ttf",
108-
".eot",
109-
".otf",
110-
".fon",
111-
".fnt",
112-
".pfb",
113-
".pfm",
11450

11551
// Audio
11652
".mp3",
@@ -119,122 +55,24 @@ export const BINARY_EXTENSIONS: Set<string> = new Set([
11955
".flac",
12056
".aac",
12157
".m4a",
122-
".wma",
123-
".aiff",
124-
".aif",
125-
".au",
12658
".opus",
127-
".mid",
128-
".midi",
129-
".ra",
130-
".ram",
131-
".amr",
13259

13360
// Video
13461
".mp4",
13562
".avi",
13663
".mov",
13764
".mkv",
138-
".wmv",
139-
".flv",
14065
".webm",
14166
".m4v",
142-
".3gp",
143-
".3g2",
144-
".ogv",
145-
".mts",
146-
".m2ts",
147-
".vob",
148-
".mpg",
149-
".mpeg",
150-
".m2v",
151-
".m4p",
152-
".m4b",
153-
".m4r",
154-
".f4v",
155-
".f4a",
156-
".f4b",
157-
".f4p",
158-
".swf",
159-
".asf",
160-
".rm",
161-
".rmvb",
162-
".divx",
163-
164-
// Java / compiled bytecode
165-
".class",
166-
".jar",
167-
".war",
168-
".ear",
169-
170-
// Python compiled
171-
".pyc",
172-
".pyo",
173-
".pyd",
17467

17568
// WebAssembly
17669
".wasm",
17770

178-
// Databases / data stores
71+
// Documents / data
72+
".pdf",
17973
".sqlite",
180-
".sqlite3",
18174
".db",
182-
".db3",
183-
".s3db",
184-
".sl3",
185-
".mdb",
186-
".accdb",
187-
188-
// 3D / game assets
189-
".blend",
190-
".fbx",
191-
".obj",
192-
".dae",
193-
".3ds",
194-
".max",
195-
".ma",
196-
".mb",
197-
".stl",
198-
".glb",
199-
".gltf",
200-
".nif",
201-
".bsa",
202-
".pak",
203-
".unity",
204-
".unitypackage",
205-
206-
// Flash
207-
".swf",
208-
".fla",
209-
210-
// Disk images
211-
".vmdk",
212-
".vhd",
213-
".vdi",
214-
".qcow2",
215-
216-
// Certificates / keys
217-
".der",
218-
".cer",
219-
".crt",
220-
".p12",
221-
".pfx",
222-
".p7b",
223-
224-
// Other binary formats
225-
".nupkg",
226-
".snupkg",
227-
".rdb",
228-
".ldb",
229-
".lnk",
230-
".DS_Store",
23175
".plist",
232-
".xib",
233-
".nib",
234-
".icns",
235-
".dSYM",
236-
".map",
237-
".min",
23876
]);
23977

24078
/**

0 commit comments

Comments
 (0)