UNPKG

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