/
loader.ts
116 lines (96 loc) · 3.48 KB
/
loader.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
/**
* This file contains a Webpack loader for Linaria.
* It uses the transform.ts function to generate class names from source code,
* returns transformed code without template literals and attaches generated source maps
*/
import path from 'path';
import loaderUtils from 'loader-utils';
import enhancedResolve from 'enhanced-resolve';
import type { RawSourceMap } from 'source-map';
import * as EvalCache from './babel/eval-cache';
import Module from './babel/module';
import { debug } from './babel/utils/logger';
import transform from './transform';
import { addFile } from './outputCssLoader';
type LoaderContext = Parameters<typeof loaderUtils.getOptions>[0];
const outputCssLoader = require.resolve('./outputCssLoader');
export default function loader(
this: LoaderContext,
content: string,
inputSourceMap: RawSourceMap | null
) {
debug('loader', this.resourcePath);
EvalCache.clearForFile(this.resourcePath);
const {
sourceMap = undefined,
preprocessor = undefined,
extension = '.linaria.css',
...rest
} = loaderUtils.getOptions(this) || {};
const outputFilename = this.resourcePath.replace(/\.[^.]+$/, extension);
const resolveOptions = {
extensions: ['.js', '.jsx', '.ts', '.tsx', '.json'],
};
const resolveSync = enhancedResolve.create.sync(
// this._compilation is a deprecated API
// However there seems to be no other way to access webpack's resolver
// There is this.resolve, but it's asynchronous
// Another option is to read the webpack.config.js, but it won't work for programmatic usage
// This API is used by many loaders/plugins, so hope we're safe for a while
this._compilation?.options.resolve
? {
...resolveOptions,
alias: this._compilation.options.resolve.alias,
modules: this._compilation.options.resolve.modules,
}
: resolveOptions
);
let result;
const originalResolveFilename = Module._resolveFilename;
try {
// Use webpack's resolution when evaluating modules
Module._resolveFilename = (id, { filename }) => {
const result = resolveSync(path.dirname(filename), id);
this.addDependency(result);
return result;
};
result = transform(content, {
filename: path.relative(process.cwd(), this.resourcePath),
inputSourceMap: inputSourceMap ?? undefined,
pluginOptions: rest,
preprocessor,
});
} finally {
// Restore original behaviour
Module._resolveFilename = originalResolveFilename;
}
if (result.cssText) {
let { cssText } = result;
if (sourceMap) {
cssText += `/*# sourceMappingURL=data:application/json;base64,${Buffer.from(
result.cssSourceMapText || ''
).toString('base64')}*/`;
}
if (result.dependencies?.length) {
result.dependencies.forEach((dep) => {
try {
const f = resolveSync(path.dirname(this.resourcePath), dep);
this.addDependency(f);
} catch (e) {
// eslint-disable-next-line no-console
console.warn(`[linaria] failed to add dependency for: ${dep}`, e);
}
});
}
addFile(this.resourcePath, cssText);
const request = `${outputFilename}!=!${outputCssLoader}!${this.resourcePath}`;
const stringifiedRequest = loaderUtils.stringifyRequest(this, request);
this.callback(
null,
`${result.code}\n\nrequire(${stringifiedRequest});`,
result.sourceMap ?? undefined
);
return;
}
this.callback(null, result.code, result.sourceMap ?? undefined);
}