generated from ryoppippi/deno-jsr-template
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmod.ts
158 lines (142 loc) · 4.56 KB
/
mod.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
import process from "node:process";
import * as path from "pathe";
import * as fs from "node:fs/promises";
import MagicString, { Bundle } from "magic-string";
import queryString from "query-string";
import cssSelectorExtract from "css-selector-extract";
import type { PreprocessorGroup } from "svelte/compiler";
import type { Config as SvelteKitConfig } from "@sveltejs/kit";
import type { UserConfig as ViteConfig } from "vite";
import { loadConfig } from "unconfig";
import { withTrailingSlash } from "ufo";
async function loadAliases() {
const { config } = await loadConfig({
merge: true,
sources: [
{
files: "svelte.config",
rewrite: (_config) => {
const config = _config as SvelteKitConfig;
return { alias: config?.kit?.alias };
},
},
{
files: "vite.config",
rewrite: (_config) => {
const config = _config as ViteConfig;
return { alias: config?.resolve?.alias };
},
},
],
});
return config?.alias ?? {};
}
/**
* Resolve paths
*/
async function getAbsPath(
{ filename, file }: { filename?: string; file: string },
) {
const aliases = await loadAliases();
const dirname = filename ? path.dirname(filename) : process.cwd();
if (path.isAbsolute(file)) {
return file;
}
if (file.startsWith("./") || file.startsWith("../")) {
return path.resolve(dirname, file);
}
for (const [alias, aliasPath] of Object.entries(aliases)) {
if (file.startsWith(withTrailingSlash(alias)) || file === alias) {
const s = new MagicString(file);
s.overwrite(0, alias.length, withTrailingSlash(aliasPath));
return path.resolve(s.toString());
}
}
return file;
}
/**
* Make `@import "./whatever.css" scoped;` statements import CSS into the component's CSS scope
*/
function matchAllImports(str: string) {
const globalRegex = /@import\s+(".*"|'.*')\s+scoped\s*;/g;
const matches = [];
let match: ReturnType<typeof globalRegex.exec>;
// eslint-disable-next-line no-cond-assign
while ((match = globalRegex.exec(str)) !== null) {
const start = match.index;
const end = start + match[0].length;
const {
url: file,
query,
} = queryString.parseUrl(match[1].substring(1, match[1].length - 1));
matches.push({
start,
end,
file,
query,
});
}
return matches;
}
/**
* Make `@import "./whatever.css" scoped;` statements import CSS into the component's CSS scope
* originally from https://github.com/sveltejs/svelte/issues/7125#issuecomment-1528965643
*/
export function importCSSPreprocess(): PreprocessorGroup {
return {
name: "import-css-scoped",
style: async function ({ content, filename }) {
const imports = matchAllImports(content);
if (imports.length > 0) {
let lastStart: number | undefined = undefined;
const state = new MagicString(content, { filename });
const remove = (start: number, end: number) =>
state.clone().remove(start, end);
const out = [];
const deps = [];
for (const { start, end, file, query } of imports.reverse()) {
// Right
if (lastStart != null) {
out.push(remove(lastStart, content.length).remove(0, end));
} else {
out.push(remove(0, end));
}
const absPath = await getAbsPath({ filename, file });
deps.push(absPath);
const cssText = (await fs.readFile(absPath)).toString();
let replaceText: string | undefined = undefined;
if (Object.keys(query).length > 0) {
const filters = Object.entries(query).map(([key, value]) => ({
selector: key,
replacement: value ?? undefined,
}));
const selectedCss: string = await cssSelectorExtract.process({
css: cssText,
filters,
});
replaceText = selectedCss;
} else {
replaceText = cssText;
}
out.push(new MagicString(replaceText, { filename: absPath }));
lastStart = start;
}
// Left
if (lastStart == null) throw new Error("lastStart should be null");
const first = remove(lastStart, content.length);
const bundle = new Bundle();
bundle.addSource(first);
for (let i = out.length - 1; i >= 0; i--) {
bundle.addSource(out[i]);
}
return {
code: bundle.toString(),
map: bundle.generateMap(),
dependencies: deps,
};
} else {
return { code: content };
}
},
};
}