UNPKG

8.34 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.default = void 0;
7
8var _path = require("path");
9
10var _pluginSyntaxJsx = _interopRequireDefault(require("@babel/plugin-syntax-jsx"));
11
12var _types = _interopRequireDefault(require("@babel/types"));
13
14var _ajvKeywords = _interopRequireDefault(require("ajv-keywords"));
15
16var _ajv = _interopRequireDefault(require("ajv"));
17
18var _optionsSchema = _interopRequireDefault(require("./schemas/optionsSchema.json"));
19
20var _optionsDefaults = _interopRequireDefault(require("./schemas/optionsDefaults"));
21
22var _createObjectExpression = _interopRequireDefault(require("./createObjectExpression"));
23
24var _requireCssModule = _interopRequireDefault(require("./requireCssModule"));
25
26var _resolveStringLiteral = _interopRequireDefault(require("./resolveStringLiteral"));
27
28var _replaceJsxExpressionContainer = _interopRequireDefault(require("./replaceJsxExpressionContainer"));
29
30var _attributeNameExists = _interopRequireDefault(require("./attributeNameExists"));
31
32function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
33
34const ajv = new _ajv.default({
35 // eslint-disable-next-line id-match
36 $data: true
37});
38(0, _ajvKeywords.default)(ajv);
39const validate = ajv.compile(_optionsSchema.default);
40
41var _default = ({
42 types: t
43}) => {
44 const filenameMap = {};
45 let skip = false;
46
47 const setupFileForRuntimeResolution = (path, filename) => {
48 const programPath = path.findParent(parentPath => {
49 return parentPath.isProgram();
50 });
51 filenameMap[filename].importedHelperIndentifier = programPath.scope.generateUidIdentifier('getClassName');
52 filenameMap[filename].styleModuleImportMapIdentifier = programPath.scope.generateUidIdentifier('styleModuleImportMap');
53 programPath.unshiftContainer('body', t.importDeclaration([t.importDefaultSpecifier(filenameMap[filename].importedHelperIndentifier)], t.stringLiteral('babel-plugin-react-css-modules/dist/browser/getClassName')));
54 const firstNonImportDeclarationNode = programPath.get('body').find(node => {
55 return !t.isImportDeclaration(node);
56 });
57 firstNonImportDeclarationNode.insertBefore(t.variableDeclaration('const', [t.variableDeclarator(filenameMap[filename].styleModuleImportMapIdentifier, (0, _createObjectExpression.default)(t, filenameMap[filename].styleModuleImportMap))])); // eslint-disable-next-line no-console
58 // console.log('setting up', filename, util.inspect(filenameMap,{depth: 5}))
59 };
60
61 const addWebpackHotModuleAccept = path => {
62 const test = t.memberExpression(t.identifier('module'), t.identifier('hot'));
63 const consequent = t.blockStatement([t.expressionStatement(t.callExpression(t.memberExpression(t.memberExpression(t.identifier('module'), t.identifier('hot')), t.identifier('accept')), [t.stringLiteral(path.node.source.value), t.functionExpression(null, [], t.blockStatement([t.expressionStatement(t.callExpression(t.identifier('require'), [t.stringLiteral(path.node.source.value)]))]))]))]);
64 const programPath = path.findParent(parentPath => {
65 return parentPath.isProgram();
66 });
67 const firstNonImportDeclarationNode = programPath.get('body').find(node => {
68 return !t.isImportDeclaration(node);
69 });
70 const hotAcceptStatement = t.ifStatement(test, consequent);
71
72 if (firstNonImportDeclarationNode) {
73 firstNonImportDeclarationNode.insertBefore(hotAcceptStatement);
74 } else {
75 programPath.pushContainer('body', hotAcceptStatement);
76 }
77 };
78
79 const getTargetResourcePath = (path, stats) => {
80 const targetFileDirectoryPath = (0, _path.dirname)(stats.file.opts.filename);
81
82 if (path.node.source.value.startsWith('.')) {
83 return (0, _path.resolve)(targetFileDirectoryPath, path.node.source.value);
84 }
85
86 return require.resolve(path.node.source.value);
87 };
88
89 const isFilenameExcluded = (filename, exclude) => {
90 return filename.match(new RegExp(exclude));
91 };
92
93 const notForPlugin = (path, stats) => {
94 stats.opts.filetypes = stats.opts.filetypes || {};
95 const extension = path.node.source.value.lastIndexOf('.') > -1 ? path.node.source.value.substr(path.node.source.value.lastIndexOf('.')) : null;
96
97 if (extension !== '.css' && Object.keys(stats.opts.filetypes).indexOf(extension) < 0) {
98 return true;
99 }
100
101 const filename = getTargetResourcePath(path, stats);
102
103 if (stats.opts.exclude && isFilenameExcluded(filename, stats.opts.exclude)) {
104 return true;
105 }
106
107 return false;
108 };
109
110 return {
111 inherits: _pluginSyntaxJsx.default,
112 visitor: {
113 ImportDeclaration(path, stats) {
114 if (skip || notForPlugin(path, stats)) {
115 return;
116 }
117
118 const filename = stats.file.opts.filename;
119 const targetResourcePath = getTargetResourcePath(path, stats);
120 let styleImportName;
121
122 if (path.node.specifiers.length === 0) {
123 // use imported file path as import name
124 styleImportName = path.node.source.value;
125 } else if (path.node.specifiers.length === 1) {
126 styleImportName = path.node.specifiers[0].local.name;
127 } else {
128 // eslint-disable-next-line no-console
129 console.warn('Please report your use case. https://github.com/gajus/babel-plugin-react-css-modules/issues/new?title=Unexpected+use+case.');
130 throw new Error('Unexpected use case.');
131 }
132
133 filenameMap[filename].styleModuleImportMap[styleImportName] = (0, _requireCssModule.default)(targetResourcePath, {
134 context: stats.opts.context,
135 filetypes: stats.opts.filetypes || {},
136 generateScopedName: stats.opts.generateScopedName
137 });
138
139 if (stats.opts.webpackHotModuleReloading) {
140 addWebpackHotModuleAccept(path);
141 }
142
143 if (stats.opts.removeImport) {
144 path.remove();
145 }
146 },
147
148 JSXElement(path, stats) {
149 if (skip) {
150 return;
151 }
152
153 const filename = stats.file.opts.filename;
154
155 if (stats.opts.exclude && isFilenameExcluded(filename, stats.opts.exclude)) {
156 return;
157 }
158
159 let attributeNames = _optionsDefaults.default.attributeNames;
160
161 if (stats.opts && stats.opts.attributeNames) {
162 attributeNames = Object.assign({}, attributeNames, stats.opts.attributeNames);
163 }
164
165 const attributes = path.node.openingElement.attributes.filter(attribute => {
166 return typeof attribute.name !== 'undefined' && typeof attributeNames[attribute.name.name] === 'string';
167 });
168
169 if (attributes.length === 0) {
170 return;
171 }
172
173 const handleMissingStyleName = stats.opts && stats.opts.handleMissingStyleName || _optionsDefaults.default.handleMissingStyleName;
174 const autoResolveMultipleImports = stats.opts && stats.opts.autoResolveMultipleImports || _optionsDefaults.default.autoResolveMultipleImports;
175
176 for (const attribute of attributes) {
177 const destinationName = attributeNames[attribute.name.name];
178 const options = {
179 autoResolveMultipleImports,
180 handleMissingStyleName
181 };
182
183 if (t.isStringLiteral(attribute.value)) {
184 (0, _resolveStringLiteral.default)(path, filenameMap[filename].styleModuleImportMap, attribute, destinationName, options);
185 } else if (t.isJSXExpressionContainer(attribute.value)) {
186 if (!filenameMap[filename].importedHelperIndentifier) {
187 setupFileForRuntimeResolution(path, filename);
188 }
189
190 (0, _replaceJsxExpressionContainer.default)(t, path, attribute, destinationName, filenameMap[filename].importedHelperIndentifier, filenameMap[filename].styleModuleImportMapIdentifier, options);
191 }
192 }
193 },
194
195 Program(path, stats) {
196 if (!validate(stats.opts)) {
197 // eslint-disable-next-line no-console
198 console.error(validate.errors);
199 throw new Error('Invalid configuration');
200 }
201
202 const filename = stats.file.opts.filename;
203 filenameMap[filename] = {
204 styleModuleImportMap: {}
205 };
206
207 if (stats.opts.skip && !(0, _attributeNameExists.default)(path, stats)) {
208 skip = true;
209 }
210 }
211
212 }
213 };
214};
215
216exports.default = _default;
217//# sourceMappingURL=index.js.map
\No newline at end of file