-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
149 lines (122 loc) · 3.76 KB
/
index.js
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
import * as path from "path";
import njk from "nunjucks";
import { createFilter } from "vite";
import escapeStringRegexp from "escape-string-regexp";
import { transform } from "esbuild";
import { glob } from "glob";
const DIRNAME = path.dirname(new URL(import.meta.url).pathname);
const RUNTIME = path.resolve(DIRNAME, "./runtime-dist/bundle.js");
const NJK_SHIM = "virtual:njk-serverless";
const RESOLVED_NJK_SHIM = "\0" + NJK_SHIM;
/**
*
* @typedef {{ dir: string, resolveMacro: (x: string) => string }} ComponentLibrary
*
* @param {{
* templates: string[],
* include?: import('vite').FilterPattern,
* filters?: Record<string, string>,
* exclude?: import('vite').FilterPattern,
* }} options
* @returns { import('vite').Plugin }
*/
export default function nunjucksLoader({
templates,
include = [],
filters = [],
exclude = [],
}) {
var filter = createFilter(include, exclude);
/** @type { import('vite').Plugin } */
const component = {
name: "vite-plugin-nunjucks-loader",
resolveId(id) {
if (id === NJK_SHIM) {
return RESOLVED_NJK_SHIM;
}
return null;
},
async load(id) {
if (id === RESOLVED_NJK_SHIM) {
for (const templateDir of templates) {
for (const src of await glob(`${templateDir}/**/*.njk`)) {
this.addWatchFile(path.resolve(src));
}
}
const env = new njk.Environment(new njk.NodeResolveLoader());
const paths = templates.map(
(dir) => `^${escapeStringRegexp(dir)}/.*\.njk`
);
for (const name of Object.keys(filters ?? {})) {
env.addFilter(name, () => {}, true);
}
const jsTemplate = njk.precompile(process.cwd(), {
env: env,
include: paths,
});
const transformRes = await transform(jsTemplate, {
define: {
window: "globalThis",
},
});
const filterInclude = Object.entries(filters ?? {})
.flatMap(([key, val]) => [
`import filter__${key} from ${JSON.stringify(path.resolve(val))};`,
`filters[${JSON.stringify(key)}] = filter__${key};`,
])
.join("\n");
return `
import { filters } from "${RUNTIME}";
${filterInclude}
${transformRes.code}
`;
}
return null;
},
async transform(code, id) {
if (!filter(id) || !isNunjucksFile(id)) {
return null;
}
const output = [
`import "${NJK_SHIM}"`,
`import render from "${RUNTIME}"`,
];
for (const dir of templates) {
const absDir = path.resolve(dir);
if (id.startsWith(absDir)) {
const templateName = trimNunjucksPath(id);
output.push(
`export default (ctx) => render(${JSON.stringify(
templateName
)}, ctx)`
);
return output.join(";\n");
}
}
this.error(
`No template directory matches ${id}. Have you configured your 'templates' setting correctly on the nunjucks loader?`
);
},
async handleHotUpdate({ server, file, modules, timestamp }) {
if (!isNunjucksFile(file)) {
return modules;
}
/**
* This is potentially inefficient, but need to figure out how to only invalidate the njk module.
*
* That said, templates are likely a high percentage of vite's build time and we'd likely need to invalidate all
* templates at least.
*/
server.moduleGraph.invalidateAll();
return modules;
},
};
return component;
}
const NJ_REGEXP = /\.njk(\?.+)?$/;
function isNunjucksFile(path) {
return NJ_REGEXP.test(path);
}
function trimNunjucksPath(p) {
return path.relative(process.cwd(), p.replace(NJ_REGEXP, ".njk"));
}