Skip to content

Commit fb75ace

Browse files
refactor: import at-rule
1 parent b931095 commit fb75ace

File tree

4 files changed

+112
-169
lines changed

4 files changed

+112
-169
lines changed

src/index.js

Lines changed: 24 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import {
99
getRemainingRequest,
1010
getCurrentRequest,
1111
stringifyRequest,
12-
isUrlRequest,
13-
urlToRequest,
1412
} from 'loader-utils';
1513
import postcss from 'postcss';
1614
import postcssPkg from 'postcss/package.json';
@@ -22,21 +20,6 @@ import icssPlugin from './plugins/icss';
2220
import Warning from './Warning';
2321
import SyntaxError from './SyntaxError';
2422

25-
const runtimeApi = require.resolve('./runtime/api');
26-
const runtimeEscape = require.resolve('./runtime/escape');
27-
28-
function getImportPrefix(loaderContext, importLoaders) {
29-
const loadersRequest = loaderContext.loaders
30-
.slice(
31-
loaderContext.loaderIndex,
32-
loaderContext.loaderIndex + 1 + importLoaders
33-
)
34-
.map((x) => x.request)
35-
.join('!');
36-
37-
return `-!${loadersRequest}!`;
38-
}
39-
4023
export default function loader(content, map, meta) {
4124
const options = getOptions(this) || {};
4225

@@ -61,7 +44,7 @@ export default function loader(content, map, meta) {
6144
}
6245

6346
if (importOpt) {
64-
plugins.push(importPlugin());
47+
plugins.push(importPlugin({ importLoaders }));
6548
}
6649

6750
plugins.push(icssPlugin());
@@ -136,147 +119,39 @@ export default function loader(content, map, meta) {
136119
newMap = JSON.stringify(newMap);
137120
}
138121

139-
let hasURLEscapeRuntime = false;
140-
let moduleCode = JSON.stringify(result.css);
141-
const imports = [];
142-
const exports = [];
122+
let moduleObj = {
123+
runtime: `module.exports = exports = require(${stringifyRequest(
124+
this,
125+
require.resolve('./runtime/api')
126+
)})(${!!sourceMap});`,
127+
imports: [],
128+
module: `exports.push([module.id, ${JSON.stringify(result.css)}, ""${
129+
newMap ? `,${newMap}` : ''
130+
}]);`,
131+
exports: [],
132+
};
143133

144134
if (result.messages && result.messages.length > 0) {
145-
let exportedTokens = {};
146-
147135
result.messages
148-
.filter((message) => (message.type === 'export' ? message : false))
136+
.filter((message) => (message.type === 'module' ? message : false))
149137
.forEach((message) => {
150-
exportedTokens = Object.assign(
151-
{},
152-
exportedTokens,
153-
message.tokens || {}
154-
);
155-
});
156-
157-
let importedTokens = {};
158-
159-
result.messages
160-
.filter((message) => (message.type === 'import' ? message : false))
161-
.forEach((message) => {
162-
importedTokens = Object.assign(
163-
{},
164-
importedTokens,
165-
message.tokens || {}
166-
);
167-
});
168-
169-
let exportsCode =
170-
Object.keys(exportedTokens).length > 0
171-
? JSON.stringify(exportedTokens)
172-
: '';
173-
174-
Object.keys(importedTokens).forEach((token) => {
175-
const value = importedTokens[token];
176-
const isUrlToken =
177-
Object.keys(value).length === 1 &&
178-
value[Object.keys(value)[0]] === 'default';
179-
const splittedToken = token.split(/(\?)?#/);
180-
const [normalizedToken] = splittedToken;
181-
182-
if (isUrlToken) {
183-
// URLs in `url` function
184-
hasURLEscapeRuntime = true;
185-
186-
const [placeholder] = Object.keys(value);
187-
188-
imports.push(
189-
`var ${placeholder} = escape(require(${stringifyRequest(
190-
this,
191-
urlToRequest(normalizedToken)
192-
)}));`
193-
);
194-
} else {
195-
const media = value['{media}'] || '';
196-
197-
if (isUrlRequest(token)) {
198-
// Requestable url in `@import` at-rule (`@import './style.css`)
199-
imports.push(
200-
`exports.i(require(${stringifyRequest(
201-
this,
202-
getImportPrefix(this, importLoaders) +
203-
urlToRequest(normalizedToken)
204-
)}), ${JSON.stringify(media)});`
205-
);
206-
} else {
207-
// Absolute url in `@import` at-rule (`@import 'https://example.com/style.css`)
208-
imports.push(
209-
`exports.push([module.id, ${JSON.stringify(
210-
`@import url(${normalizedToken});`
211-
)}, ${JSON.stringify(media)}]);`
212-
);
213-
}
214-
}
215-
216-
Object.keys(value).forEach((replacedToken) => {
217-
if (['{media}', '{type}'].includes(replacedToken)) {
218-
return;
138+
try {
139+
moduleObj = message.modify(moduleObj, this);
140+
} catch (err) {
141+
this.emitError(err);
219142
}
220-
221-
let replacedCode = null;
222-
223-
if (isUrlToken) {
224-
// Code for `url` tokens
225-
replacedCode = `" + ${replacedToken} + "${
226-
splittedToken[1] ? splittedToken[1] : ''
227-
}${splittedToken[2] ? `#${splittedToken[2]}` : ''}`;
228-
} else {
229-
// Code for `local` tokens
230-
replacedCode = `" + require(${stringifyRequest(
231-
this,
232-
getImportPrefix(this, importLoaders) +
233-
urlToRequest(normalizedToken)
234-
)}).locals[${JSON.stringify(value[replacedToken])}] +"`;
235-
}
236-
237-
moduleCode = moduleCode.replace(
238-
new RegExp(replacedToken, 'g'),
239-
replacedCode
240-
);
241-
exportsCode = exportsCode.replace(
242-
new RegExp(replacedToken, 'g'),
243-
replacedCode
244-
);
245143
});
246-
});
247-
248-
if (exportsCode) {
249-
exports.push(`exports.locals = ${exportsCode}`);
250-
}
251144
}
252145

253-
cb(
146+
const { runtime, imports, module, exports } = moduleObj;
147+
148+
return cb(
254149
null,
255150
[
256-
...(hasURLEscapeRuntime
257-
? [
258-
'// CSS runtime escape',
259-
`var escape = require(${stringifyRequest(
260-
this,
261-
runtimeEscape
262-
)});`,
263-
'',
264-
]
265-
: []),
266-
'// CSS runtime',
267-
`module.exports = exports = require(${stringifyRequest(
268-
this,
269-
runtimeApi
270-
)})(${!!sourceMap});`,
271-
'',
272-
...(imports.length > 0
273-
? ['// CSS imports', imports.join('\n'), '']
274-
: []),
275-
'// CSS module',
276-
`exports.push([module.id, ${moduleCode}, ""${
277-
newMap ? `,${newMap}` : ''
278-
}]);\n`,
279-
...(exports.length > 0 ? ['// CSS exports', exports.join('\n')] : []),
151+
runtime ? `// CSS runtime\n${runtime}` : '',
152+
imports.length > 0 ? `\n// CSS imports\n${imports.join('\n')}` : '',
153+
module ? `\n// CSS module\n${module}` : '',
154+
exports.length > 0 ? `// CSS exports\n${exports.join('\n')}` : '',
280155
].join('\n')
281156
);
282157
})

src/plugins/import.js

Lines changed: 55 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
11
import postcss from 'postcss';
22
import valueParser from 'postcss-value-parser';
3+
import { stringifyRequest, isUrlRequest, urlToRequest } from 'loader-utils';
34

4-
import utils from './utils';
5+
function normalizeUrl(url) {
6+
return url.split(/(\?)?#/);
7+
}
8+
9+
function getImportPrefix(loaderContext, importLoaders) {
10+
const loadersRequest = loaderContext.loaders
11+
.slice(
12+
loaderContext.loaderIndex,
13+
loaderContext.loaderIndex + 1 + importLoaders
14+
)
15+
.map((x) => x.request)
16+
.join('!');
17+
18+
return `-!${loadersRequest}!`;
19+
}
520

621
const pluginName = 'postcss-css-loader-import';
722

@@ -43,8 +58,9 @@ const parseImport = (params) => {
4358

4459
export default postcss.plugin(
4560
pluginName,
46-
() =>
61+
(options) =>
4762
function process(css, result) {
63+
const { importLoaders } = options;
4864
const imports = {};
4965

5066
css.walkAtRules(/^import$/i, (atrule) => {
@@ -74,14 +90,45 @@ export default postcss.plugin(
7490
atrule.remove();
7591

7692
imports[
77-
`'${parsed.url}'${
78-
parsed.media.length === 0
79-
? ''
80-
: ` ${parsed.media.trim().toLowerCase()}`
93+
`"${parsed.url}"${
94+
parsed.media ? ` "${parsed.media.toLowerCase()}"` : ''
8195
}`
82-
] = {};
96+
] = parsed;
8397
});
8498

85-
css.prepend(utils.createICSSImports(imports));
99+
Object.keys(imports).forEach((token) => {
100+
const importee = imports[token];
101+
102+
result.messages.push({
103+
pluginName,
104+
type: 'module',
105+
modify(moduleObj, loaderContext) {
106+
const { url, media } = importee;
107+
108+
if (isUrlRequest(url)) {
109+
// Remove `#` from `require`
110+
const [normalizedUrl] = normalizeUrl(url);
111+
112+
// Requestable url in `@import` at-rule (`@import './style.css`)
113+
moduleObj.imports.push(
114+
`exports.i(require(${stringifyRequest(
115+
loaderContext,
116+
getImportPrefix(loaderContext, importLoaders) +
117+
urlToRequest(normalizedUrl)
118+
)}), ${JSON.stringify(media)});`
119+
);
120+
} else {
121+
// Absolute url in `@import` at-rule (`@import 'https://example.com/style.css`)
122+
moduleObj.imports.push(
123+
`exports.push([module.id, ${JSON.stringify(
124+
`@import url(${url});`
125+
)}, ${JSON.stringify(media)}]);`
126+
);
127+
}
128+
129+
return moduleObj;
130+
},
131+
});
132+
});
86133
}
87134
);

0 commit comments

Comments
 (0)