Skip to content

Commit 0f9f0a5

Browse files
committed
Refactor hoisting declaration function
1 parent 466ae41 commit 0f9f0a5

File tree

4 files changed

+79
-64
lines changed

4 files changed

+79
-64
lines changed

src/hoister.js

Lines changed: 63 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,90 @@
1-
import path from "path/posix";
21
import { getProxiedObject } from "./helper.js";
32

43
const foundInLayout = (layoutClassList, className) => {
5-
for (const key in layoutClassList) {
6-
for (const layoutClassName in layoutClassList[key]) {
7-
if (layoutClassName === className) {
4+
for (const original in layoutClassList) {
5+
for (const minified in layoutClassList[original]) {
6+
if (minified === className) {
87
return true;
98
}
109
}
1110
}
1211
return false;
1312
};
1413

15-
export const hoistClass = (classCache, hoistedClassCache) => {
16-
const layoutFilename = "__layout.svelte";
14+
const removeComponentDecl = (layoutFilename, base, declarationCache) => {
15+
if (base === layoutFilename) {
16+
return declarationCache;
17+
}
18+
19+
return {};
20+
};
1721

18-
// no need for in loop here for filename?
19-
for (const filename in classCache) {
20-
const { dir, base } = path.parse(filename);
22+
const hasReachedSrc = (path) => {
23+
const dirList = path.split("/");
24+
return dirList[dirList.length - 1] === "src";
25+
};
2126

22-
const currentComponentClassList = classCache[filename];
27+
const goPreviousDir = (path) => {
28+
return path.substring(0, path.lastIndexOf("/"));
29+
};
2330

24-
if (base === layoutFilename) {
25-
hoistedClassCache[dir][base] = currentComponentClassList;
26-
continue;
31+
const findClosestLayout = (dir, layoutFilename, classCache) => {
32+
if (classCache[dir][layoutFilename]) {
33+
return classCache[dir][layoutFilename];
34+
} else {
35+
if (hasReachedSrc(dir)) {
36+
return false;
2737
}
38+
return findClosestLayout(goPreviousDir(dir), layoutFilename, classCache);
39+
}
40+
};
2841

29-
const layoutClassList = hoistedClassCache[dir][layoutFilename];
42+
export const hoistDeclaration = (
43+
opts,
44+
{ dir: curDir, base: curBase },
45+
classCache,
46+
declarationCache
47+
) => {
48+
if (!opts.lazyLoad) {
49+
return removeComponentDecl(opts.layoutFilename, curBase, declarationCache);
50+
}
3051

31-
if (!layoutClassList) {
32-
hoistedClassCache[dir][base] = currentComponentClassList;
33-
continue;
34-
}
52+
if (curBase === opts.layoutFilename) {
53+
return declarationCache;
54+
}
55+
56+
const currentComponentDecl = classCache[curDir][curBase];
57+
const layoutDecl = findClosestLayout(curDir, opts.layoutFilename, classCache);
58+
59+
// hoisting won't work without a __layout.svelte
60+
if (!layoutDecl) {
61+
return declarationCache;
62+
}
3563

36-
const filteredClassList = getProxiedObject();
64+
const list = [];
3765

38-
for (const original in currentComponentClassList) {
39-
for (const minified in currentComponentClassList[original]) {
40-
if (!foundInLayout(layoutClassList, minified)) {
41-
filteredClassList[original][minified] = true;
42-
}
66+
for (const original in currentComponentDecl) {
67+
for (const minified in currentComponentDecl[original]) {
68+
if (!foundInLayout(layoutDecl, minified)) {
69+
list.push(minified);
4370
}
4471
}
45-
46-
hoistedClassCache[dir][base] = filteredClassList;
4772
}
73+
74+
return filterDeclaration(list, declarationCache);
4875
};
4976

50-
export const hoistDeclaration = (
51-
filename,
52-
hoistedClassCache,
53-
declarationCache
54-
) => {
55-
const { dir, base } = path.parse(filename);
56-
const hoistedDeclarationCache = getProxiedObject();
57-
const currentComponentCache = hoistedClassCache[dir][base];
58-
// This is hell
59-
for (const original in currentComponentCache) {
60-
for (const minified of Object.keys(currentComponentCache[original])) {
61-
for (const mediaQuery in declarationCache) {
62-
for (const decl in declarationCache[mediaQuery]) {
63-
if (minified === declarationCache[mediaQuery][decl]) {
64-
hoistedDeclarationCache[mediaQuery][decl] = minified;
65-
}
66-
}
77+
const filterDeclaration = (list, declarationCache) => {
78+
const result = getProxiedObject();
79+
80+
for (const mediaQuery in declarationCache) {
81+
for (const originalDecl in declarationCache[mediaQuery]) {
82+
const token = declarationCache[mediaQuery][originalDecl];
83+
if (list.includes(token)) {
84+
result[mediaQuery][originalDecl] = token;
6785
}
6886
}
6987
}
70-
return hoistedDeclarationCache;
88+
89+
return result;
7190
};

src/index.js

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,17 @@ import { parse } from "svelte/compiler";
22
import createTokenizer from "./tokenizer.js";
33
import createTransformer from "./transformer.js";
44
import { getProxiedObject } from "./helper.js";
5-
import { hoistDeclaration, hoistClass } from "./hoister.js";
5+
import { hoistDeclaration } from "./hoister.js";
6+
import path from "path";
67

78
// Keep state outside default function as it will be called multiple times
89
const classCache = getProxiedObject();
9-
const hoistedClassCache = getProxiedObject();
1010
const declarationCache = getProxiedObject();
1111
const tokernizer = createTokenizer(classCache, declarationCache);
1212

1313
const defaultOpts = {
14-
lazyLoad: true
14+
lazyLoad: true,
15+
layoutFilename: "__layout.svelte"
1516
}
1617

1718
export default function (opts = {}) {
@@ -20,21 +21,17 @@ export default function (opts = {}) {
2021
markup: function ({ content, filename }) {
2122
// Ignore all default code
2223
if (!filename.includes(".svelte-kit")) {
24+
const parsedPath = path.parse(filename)
2325
const ast = parse(content, { filename });
24-
tokernizer.generateToken(ast.css, filename);
26+
tokernizer.generateToken(ast.css, parsedPath);
2527

26-
const transformer = createTransformer(content, filename, opts);
28+
const transformer = createTransformer(content, parsedPath);
29+
30+
const hoisted = hoistDeclaration(opts, parsedPath, classCache, declarationCache)
2731

28-
hoistClass(classCache, hoistedClassCache);
29-
const hoistedDeclarationCache = hoistDeclaration(
30-
filename,
31-
hoistedClassCache,
32-
declarationCache
33-
);
34-
3532
const result = transformer
3633
.transformHtml(ast.html, classCache)
37-
.transformCss(ast.css, hoistedDeclarationCache)
34+
.transformCss(ast.css, hoisted)
3835
.toString();
3936

4037
return {

src/tokenizer.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const createTokenizer = (classCache, declarationCache) => {
4343
const next = joli("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_");
4444

4545
return {
46-
generateToken(cssAst, filename) {
46+
generateToken(cssAst, {dir, base}) {
4747
const tempCache = getProxiedObject();
4848

4949
walk(cssAst, {
@@ -83,11 +83,10 @@ const createTokenizer = (classCache, declarationCache) => {
8383
}
8484
},
8585
});
86-
86+
8787
// hydrate the classCache once only here
88-
classCache[filename] = {};
89-
Object.assign(classCache[filename], tempCache);
90-
// cannot dedup classCache directly, as it is used to reference class in HTML
88+
classCache[dir][base] = {};
89+
Object.assign(classCache[dir][base], tempCache);
9190
},
9291
};
9392
};

src/transformer.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import MagicString from "magic-string";
22
import { walk } from "svelte/compiler";
33
import { assembleRules } from "./helper.js";
44

5-
export default function (code, filename, opts) {
5+
export default function (code, {dir, base}) {
66
const changeable = new MagicString(code);
77

88
return {
@@ -55,7 +55,7 @@ export default function (code, filename, opts) {
5555
for (const value of attrValue.raw.split(" ")) {
5656
let minifiedClass = "";
5757
let index = 0;
58-
for (const token in cache[filename][value]) {
58+
for (const token in cache[dir][base][value]) {
5959
minifiedClass += index === 0 ? token : " " + token;
6060
index++;
6161
}

0 commit comments

Comments
 (0)