UNPKG

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