UNPKG

5.1 kBJavaScriptView Raw
1"use strict";
2
3var _crypto = _interopRequireDefault(require("crypto"));
4
5var _fs = require("fs");
6
7var _path = require("path");
8
9var _util = _interopRequireDefault(require("util"));
10
11var _v = require("v8");
12
13var _findCacheDir = _interopRequireDefault(require("find-cache-dir"));
14
15var _loaders = require("./utils/loaders");
16
17var _replaceComposes = _interopRequireDefault(require("./utils/replaceComposes"));
18
19function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
20
21const cacheDir = (0, _findCacheDir.default)({
22 name: 'astroturf-loader'
23});
24(0, _fs.mkdirSync)(cacheDir, {
25 recursive: true
26});
27const inMemoryStyleCache = new Map();
28
29const hash = name => `${_crypto.default.createHash('md4').update(name).digest('hex')}.cache`;
30
31const cache = {
32 async set(source, newStyles) {
33 const styles = (await this.get(source)) || new Map();
34 inMemoryStyleCache.set(source, styles);
35 newStyles.forEach(style => {
36 styles.set(style.identifier, style);
37 });
38 await _fs.promises.writeFile(`${cacheDir}/${hash(source)}`, (0, _v.serialize)(styles));
39 },
40
41 async get(source) {
42 let styles = inMemoryStyleCache.get(source);
43
44 if (!styles) {
45 try {
46 styles = (0, _v.deserialize)(await _fs.promises.readFile(`${cacheDir}/${hash(source)}`));
47 inMemoryStyleCache.set(source, styles);
48 } catch (err) {
49 /* ignore */
50 }
51 }
52
53 return styles;
54 }
55
56};
57
58const debug = _util.default.debuglog('astroturf:loader');
59
60module.exports = async function loader(content, _map, meta) {
61 const {
62 resourcePath
63 } = this;
64 const loaderOpts = this.getOptions() || {};
65 const cb = this.async();
66
67 const loadModule = _util.default.promisify((request, done) => this.loadModule(request, (err, _, _1, module) => done(err, module)));
68
69 if (loaderOpts.style) {
70 const styleId = this.resourceQuery.slice(1);
71 const styles = await cache.get(resourcePath);
72 let style = styles == null ? void 0 : styles.get(styleId);
73
74 if (!style) {
75 var _inMemoryStyleCache$g;
76
77 await loadModule(resourcePath);
78 style = (_inMemoryStyleCache$g = inMemoryStyleCache.get(resourcePath)) == null ? void 0 : _inMemoryStyleCache$g.get(styleId);
79 }
80
81 if (!style) {
82 return cb(new Error(`Could not resolve style ${styleId} in file ${resourcePath}`));
83 }
84
85 if (!this._module.matchResource) this._module.matchResource = style.absoluteFilePath;
86 return cb(null, style.value);
87 }
88
89 function getLoaderRequest(from, to, id) {
90 const cssBase = (0, _path.basename)(to);
91 const file = `${cssBase}!=!astroturf/inline-loader?style=1!${from}?${id}`;
92 return file;
93 }
94
95 const resolve = _util.default.promisify(this.resolve);
96
97 const dependencies = [];
98
99 const buildDependency = async request => {
100 const resource = await resolve((0, _path.dirname)(resourcePath), request);
101 return loadModule(resource);
102 };
103
104 function resolveDependency(interpolation, localStyle, node) {
105 const {
106 identifier,
107 request
108 } = interpolation;
109 if (!interpolation.identifier) return null;
110 const {
111 loc
112 } = node;
113 const memberProperty = 'property' in node && node.property.name;
114 const imported = `###ASTROTURF_IMPORTED_${dependencies.length}###`;
115 const source = `###ASTROTURF_SOURCE_${dependencies.length}###`;
116 debug(`resolving dependency: ${request}`);
117 dependencies.push(buildDependency(request).then(module => {
118 const styles = inMemoryStyleCache.get(module.resource);
119 const style = styles == null ? void 0 : styles.get(identifier);
120
121 if (!style) {
122 throw (0, _loaders.buildDependencyError)(content, interpolation, Array.from((styles == null ? void 0 : styles.values()) || []), module.resource, loc);
123 }
124
125 debug(`resolved request to: ${style.absoluteFilePath}`);
126 const styleReq = getLoaderRequest(module.resource, style.absoluteFilePath, style.identifier); // replace composes first bc we need need to use a different identifier
127
128 localStyle.value = (0, _replaceComposes.default)(localStyle.value, match => match.replace(source, styleReq).replace(imported, style.type === 'stylesheet' ? memberProperty : 'cls2')); // replace selector interpolations
129
130 localStyle.value = localStyle.value.replace(source, styleReq).replace(imported, style.type === 'stylesheet' ? memberProperty : 'cls1');
131 }));
132 return {
133 source,
134 imported
135 };
136 }
137
138 try {
139 const options = await (0, _loaders.resolveOptions)(this);
140 options.getRequirePath = getLoaderRequest;
141 const {
142 styles = [],
143 changeset
144 } = (0, _loaders.collectStyles)(content, resourcePath, resolveDependency, options);
145
146 if (meta) {
147 meta.styles = styles;
148 }
149
150 if (!styles.length) {
151 return cb(null, content);
152 }
153
154 return Promise.all(dependencies).then(async () => {
155 await cache.set(resourcePath, styles);
156 const result = (0, _loaders.replaceStyleTemplates)(this, resourcePath, content, changeset);
157 cb(null, result.code, result.map);
158 }).catch(cb);
159 } catch (err) {
160 return cb(err);
161 }
162};
\No newline at end of file