UNPKG

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