tw-migrate 0.1.0 → 0.2.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.
- package/README.md +13 -1
- package/bin/tw-migrate.js +15 -10
- package/html.js +74 -36
- package/index.d.ts +1 -1
- package/index.js +1283 -312
- package/native.js +18 -11
- package/package.json +14 -12
- package/style-compiler.js +52 -37
- package/vue.js +541 -0
- package/rfcs/batch-css-migration.md +0 -297
- package/rfcs/browser-ecosystem-e2e.md +0 -338
- package/rfcs/css-to-tailwind-migration-cli.md +0 -521
- package/rfcs/preprocessor-and-html-migration.md +0 -497
package/native.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { createRequire } from
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
2
|
|
|
3
3
|
const require = createRequire(import.meta.url);
|
|
4
4
|
const targets = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
"darwin-arm64": "darwin-arm64",
|
|
6
|
+
"darwin-x64": "darwin-x64",
|
|
7
|
+
"linux-arm64": "linux-arm64-gnu",
|
|
8
|
+
"linux-x64": "linux-x64-gnu",
|
|
9
|
+
"win32-x64": "win32-x64-msvc",
|
|
10
10
|
};
|
|
11
11
|
const target = targets[`${process.platform}-${process.arch}`];
|
|
12
12
|
|
|
@@ -21,14 +21,21 @@ for (const load of [
|
|
|
21
21
|
binding = load();
|
|
22
22
|
break;
|
|
23
23
|
} catch (error) {
|
|
24
|
-
if (error.code !==
|
|
24
|
+
if (error.code !== "MODULE_NOT_FOUND") throw error;
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
if (!binding) {
|
|
29
|
-
throw new Error(
|
|
29
|
+
throw new Error(
|
|
30
|
+
`No tw-migrate native addon was found for ${target}. Reinstall the package or build it locally.`,
|
|
31
|
+
);
|
|
30
32
|
}
|
|
31
33
|
|
|
32
|
-
export const
|
|
33
|
-
|
|
34
|
-
|
|
34
|
+
export const {
|
|
35
|
+
decodeSourceMap,
|
|
36
|
+
planBatchMigration,
|
|
37
|
+
staticImportBindings,
|
|
38
|
+
staticImports,
|
|
39
|
+
staticStringExpression,
|
|
40
|
+
validateCss,
|
|
41
|
+
} = binding;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tw-migrate",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Preview and migrate React CSS to Tailwind utilities",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Preview and migrate React, Vue, and HTML CSS to Tailwind utilities",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"codemod",
|
|
7
7
|
"css",
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"napi-rs",
|
|
11
11
|
"react",
|
|
12
12
|
"tailwind",
|
|
13
|
-
"tailwindcss"
|
|
13
|
+
"tailwindcss",
|
|
14
|
+
"vue"
|
|
14
15
|
],
|
|
15
16
|
"homepage": "https://github.com/jong-kyung/tw-migrate#readme",
|
|
16
17
|
"bugs": {
|
|
@@ -31,8 +32,8 @@
|
|
|
31
32
|
"index.js",
|
|
32
33
|
"index.d.ts",
|
|
33
34
|
"native.js",
|
|
34
|
-
"
|
|
35
|
-
"
|
|
35
|
+
"style-compiler.js",
|
|
36
|
+
"vue.js"
|
|
36
37
|
],
|
|
37
38
|
"type": "module",
|
|
38
39
|
"main": "index.js",
|
|
@@ -41,7 +42,7 @@
|
|
|
41
42
|
"build": "napi build --manifest-path crates/tw_migrate/Cargo.toml --package-json-path package.json --output-dir . --platform --release --no-js --dts binding.d.ts",
|
|
42
43
|
"build:debug": "napi build --manifest-path crates/tw_migrate/Cargo.toml --package-json-path package.json --output-dir . --platform --no-js --dts binding.d.ts",
|
|
43
44
|
"artifacts": "napi artifacts --output-dir . --npm-dir npm",
|
|
44
|
-
"test": "cargo test && pnpm build:debug &&
|
|
45
|
+
"test": "cargo test && pnpm build:debug && vp test",
|
|
45
46
|
"test:ecosystem": "node ecosystem-ci/run.ts",
|
|
46
47
|
"test:rust": "cargo test",
|
|
47
48
|
"typecheck": "tsc --noEmit",
|
|
@@ -64,14 +65,15 @@
|
|
|
64
65
|
"verdaccio": "6.8.0",
|
|
65
66
|
"vite": "catalog:",
|
|
66
67
|
"vite-plus": "catalog:",
|
|
67
|
-
"vitest": "catalog:"
|
|
68
|
+
"vitest": "catalog:",
|
|
69
|
+
"vue": "3.5.40"
|
|
68
70
|
},
|
|
69
71
|
"optionalDependencies": {
|
|
70
|
-
"tw-migrate-darwin-arm64": "0.
|
|
71
|
-
"tw-migrate-darwin-x64": "0.
|
|
72
|
-
"tw-migrate-linux-arm64-gnu": "0.
|
|
73
|
-
"tw-migrate-linux-x64-gnu": "0.
|
|
74
|
-
"tw-migrate-win32-x64-msvc": "0.
|
|
72
|
+
"tw-migrate-darwin-arm64": "0.2.0",
|
|
73
|
+
"tw-migrate-darwin-x64": "0.2.0",
|
|
74
|
+
"tw-migrate-linux-arm64-gnu": "0.2.0",
|
|
75
|
+
"tw-migrate-linux-x64-gnu": "0.2.0",
|
|
76
|
+
"tw-migrate-win32-x64-msvc": "0.2.0"
|
|
75
77
|
},
|
|
76
78
|
"napi": {
|
|
77
79
|
"binaryName": "tw-migrate",
|
package/style-compiler.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import { realpath } from
|
|
2
|
-
import { createRequire } from
|
|
3
|
-
import { basename, dirname, extname, isAbsolute, join, resolve } from
|
|
4
|
-
import { fileURLToPath, pathToFileURL } from
|
|
1
|
+
import { realpath } from "node:fs/promises";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { basename, dirname, extname, isAbsolute, join, resolve } from "node:path";
|
|
4
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
5
5
|
|
|
6
|
-
import { decodeSourceMap } from
|
|
6
|
+
import { decodeSourceMap } from "./native.js";
|
|
7
7
|
|
|
8
|
-
const SASS_EXTENSIONS = new Set([
|
|
8
|
+
const SASS_EXTENSIONS = new Set([".scss", ".sass"]);
|
|
9
9
|
|
|
10
10
|
export function isSassPath(path) {
|
|
11
11
|
return SASS_EXTENSIONS.has(extname(path));
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
export function isPreprocessorPath(path) {
|
|
15
|
-
return isSassPath(path) || extname(path) ===
|
|
15
|
+
return isSassPath(path) || extname(path) === ".less";
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
async function loadProjectModule(packageRoot, name, errorMessage) {
|
|
19
|
-
const projectRequire = createRequire(join(packageRoot,
|
|
19
|
+
const projectRequire = createRequire(join(packageRoot, "package.json"));
|
|
20
20
|
let modulePath;
|
|
21
21
|
try {
|
|
22
22
|
modulePath = projectRequire.resolve(name);
|
|
@@ -28,34 +28,36 @@ async function loadProjectModule(packageRoot, name, errorMessage) {
|
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
export function loadProjectSass(packageRoot) {
|
|
31
|
-
return loadProjectModule(packageRoot,
|
|
31
|
+
return loadProjectModule(packageRoot, "sass", "Sass must be installed in the target project.");
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
export function loadProjectLess(packageRoot) {
|
|
35
|
-
return loadProjectModule(packageRoot,
|
|
35
|
+
return loadProjectModule(packageRoot, "less", "Less must be installed in the target project.");
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export async function compileSassEntry(sass, entryPath, source) {
|
|
38
|
+
export async function compileSassEntry(sass, entryPath, source, { virtualEntry = false } = {}) {
|
|
39
39
|
const options = {
|
|
40
40
|
sourceMap: true,
|
|
41
41
|
sourceMapIncludeSources: true,
|
|
42
42
|
};
|
|
43
|
-
const result =
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
const result =
|
|
44
|
+
source === undefined
|
|
45
|
+
? await sass.compileAsync(entryPath, options)
|
|
46
|
+
: await sass.compileStringAsync(source, {
|
|
47
|
+
...options,
|
|
48
|
+
url: pathToFileURL(entryPath),
|
|
49
|
+
syntax: extname(entryPath) === ".sass" ? "indented" : "scss",
|
|
50
|
+
});
|
|
50
51
|
|
|
51
52
|
if (!result.sourceMap) throw new Error(`Sass did not produce a source map for ${entryPath}`);
|
|
52
53
|
const loadedPaths = result.loadedUrls
|
|
53
|
-
.filter((url) => url.protocol ===
|
|
54
|
+
.filter((url) => url.protocol === "file:")
|
|
54
55
|
.map(fileURLToPath);
|
|
55
56
|
const mappings = sourceMappings(result.sourceMap);
|
|
56
57
|
const normalizedPaths = await normalizeEntryPaths(
|
|
57
58
|
[...loadedPaths, ...mappings.map((mapping) => mapping.sourcePath)],
|
|
58
59
|
entryPath,
|
|
60
|
+
virtualEntry,
|
|
59
61
|
);
|
|
60
62
|
return {
|
|
61
63
|
css: result.css,
|
|
@@ -67,16 +69,22 @@ export async function compileSassEntry(sass, entryPath, source) {
|
|
|
67
69
|
};
|
|
68
70
|
}
|
|
69
71
|
|
|
70
|
-
async function normalizeEntryPaths(paths, entryPath) {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
72
|
+
async function normalizeEntryPaths(paths, entryPath, virtualEntry) {
|
|
73
|
+
// A virtual SFC block entry never exists on disk; a real entry that
|
|
74
|
+
// vanished mid-run must keep failing as a source-integrity error.
|
|
75
|
+
const canonicalEntryPath = virtualEntry ? entryPath : await realpath(entryPath);
|
|
76
|
+
return new Map(
|
|
77
|
+
await Promise.all(
|
|
78
|
+
[...new Set(paths)].map(async (path) => {
|
|
79
|
+
if (path === entryPath) return [path, path];
|
|
80
|
+
try {
|
|
81
|
+
return [path, (await realpath(path)) === canonicalEntryPath ? entryPath : path];
|
|
82
|
+
} catch {
|
|
83
|
+
return [path, path];
|
|
84
|
+
}
|
|
85
|
+
}),
|
|
86
|
+
),
|
|
87
|
+
);
|
|
80
88
|
}
|
|
81
89
|
|
|
82
90
|
export async function compileLessEntry(less, entryPath, source) {
|
|
@@ -85,21 +93,28 @@ export async function compileLessEntry(less, entryPath, source) {
|
|
|
85
93
|
sourceMap: {
|
|
86
94
|
outputSourceFiles: true,
|
|
87
95
|
sourceMapBasepath: dirname(entryPath),
|
|
88
|
-
sourceMapOutputFilename: `${basename(entryPath,
|
|
96
|
+
sourceMapOutputFilename: `${basename(entryPath, ".less")}.css`,
|
|
89
97
|
},
|
|
90
98
|
});
|
|
91
99
|
return {
|
|
92
100
|
css: result.css,
|
|
93
|
-
loadedPaths: result.imports.map((path) =>
|
|
101
|
+
loadedPaths: result.imports.map((path) =>
|
|
102
|
+
isAbsolute(path) ? path : resolve(dirname(entryPath), path),
|
|
103
|
+
),
|
|
94
104
|
sourceMappings: result.map ? sourceMappings(JSON.parse(result.map), dirname(entryPath)) : [],
|
|
95
105
|
};
|
|
96
106
|
}
|
|
97
107
|
|
|
98
108
|
export function sourceMappings(sourceMap, sourceBase) {
|
|
99
|
-
return JSON.parse(decodeSourceMap(JSON.stringify(sourceMap))).flatMap(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
109
|
+
return JSON.parse(decodeSourceMap(JSON.stringify(sourceMap))).flatMap(
|
|
110
|
+
({ source, ...mapping }) => {
|
|
111
|
+
const sourcePath = sourcePathFromMap(
|
|
112
|
+
sourceReferenceFromMap(source, sourceMap.sourceRoot),
|
|
113
|
+
sourceBase,
|
|
114
|
+
);
|
|
115
|
+
return sourcePath ? [{ ...mapping, sourcePath }] : [];
|
|
116
|
+
},
|
|
117
|
+
);
|
|
103
118
|
}
|
|
104
119
|
|
|
105
120
|
function sourceReferenceFromMap(source, sourceRoot) {
|
|
@@ -107,7 +122,7 @@ function sourceReferenceFromMap(source, sourceRoot) {
|
|
|
107
122
|
if (isAbsolute(sourceRoot)) return resolve(sourceRoot, source);
|
|
108
123
|
try {
|
|
109
124
|
const root = new URL(sourceRoot);
|
|
110
|
-
root.pathname = `${root.pathname.replace(/\/+$/,
|
|
125
|
+
root.pathname = `${root.pathname.replace(/\/+$/, "")}/`;
|
|
111
126
|
return new URL(source, root).href;
|
|
112
127
|
} catch {
|
|
113
128
|
return join(sourceRoot, source);
|
|
@@ -118,7 +133,7 @@ function sourcePathFromMap(source, sourceBase) {
|
|
|
118
133
|
if (isAbsolute(source)) return resolve(source);
|
|
119
134
|
try {
|
|
120
135
|
const url = new URL(source);
|
|
121
|
-
return url.protocol ===
|
|
136
|
+
return url.protocol === "file:" ? resolve(fileURLToPath(url)) : undefined;
|
|
122
137
|
} catch {
|
|
123
138
|
return sourceBase ? resolve(sourceBase, source) : undefined;
|
|
124
139
|
}
|