@tdb/web
Version:
Common condiguration for serving a web-site and testing web-based UI components.
210 lines (171 loc) • 7.59 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.visitor = undefined;
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
exports.processTaggedTemplateExpression = processTaggedTemplateExpression;
exports.default = function () {
return _extends({
Program: function Program(path, state) {
(0, _utils.setStateOptions)(state);
}
}, visitor);
};
var _babelTypes = require('babel-types');
var t = _interopRequireWildcard(_babelTypes);
var _constants = require('./_constants');
var _utils = require('./_utils');
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } }
var isModuleExports = t.buildMatchMemberExpression('module.exports');
function processTaggedTemplateExpression(_ref) {
var type = _ref.type,
path = _ref.path,
fileInfo = _ref.fileInfo,
splitRules = _ref.splitRules,
plugins = _ref.plugins,
vendorPrefix = _ref.vendorPrefix;
var templateLiteral = path.get('quasi');
var scope = void 0;
// Check whether there are undefined references or
// references to this.something (e.g. props or state).
// We allow dynamic styles only when resolving styles.
if (type !== 'resolve') {
(0, _utils.validateExternalExpressions)(templateLiteral);
} else if (!path.scope.path.isProgram()) {
scope = (0, _utils.getScope)(path);
}
var stylesInfo = (0, _utils.getJSXStyleInfo)(templateLiteral, scope);
var _computeClassNames = (0, _utils.computeClassNames)([stylesInfo]),
staticClassName = _computeClassNames.staticClassName,
className = _computeClassNames.className;
var styles = (0, _utils.processCss)(_extends({}, stylesInfo, {
staticClassName: staticClassName,
fileInfo: fileInfo,
isGlobal: type === 'global',
plugins: plugins,
vendorPrefix: vendorPrefix
}), { splitRules: splitRules });
if (type === 'resolve') {
var hash = styles.hash,
_css = styles.css,
expressions = styles.expressions;
path.replaceWith(
// {
// styles: <_JSXStyle ... />,
// className: 'jsx-123'
// }
t.objectExpression([t.objectProperty(t.identifier('styles'), (0, _utils.makeStyledJsxTag)(hash, _css, expressions)), t.objectProperty(t.identifier('className'), className)]));
return;
}
var id = path.parentPath.node.id;
var baseExportName = id ? id.name : 'default';
var parentPath = baseExportName === 'default' ? path.parentPath : path.findParent(function (path) {
return path.isVariableDeclaration() || path.isAssignmentExpression() && isModuleExports(path.get('left').node);
});
if (baseExportName !== 'default' && !parentPath.parentPath.isProgram()) {
parentPath = parentPath.parentPath;
}
var css = (0, _utils.cssToBabelType)(styles.css);
var newPath = t.isArrayExpression(css) ? css : t.newExpression(t.identifier('String'), [css]);
// default exports
if (baseExportName === 'default') {
var defaultExportIdentifier = path.scope.generateUidIdentifier('defaultExport');
parentPath.insertBefore(t.variableDeclaration('const', [t.variableDeclarator(defaultExportIdentifier, newPath)]));
parentPath.insertBefore(addHash(defaultExportIdentifier, styles.hash));
path.replaceWith(defaultExportIdentifier);
return;
}
// local and named exports
parentPath.insertAfter(addHash(t.identifier(baseExportName), styles.hash));
path.replaceWith(newPath);
}
function addHash(exportIdentifier, hash) {
var value = typeof hash === 'string' ? t.stringLiteral(hash) : hash;
return t.expressionStatement(t.assignmentExpression('=', t.memberExpression(exportIdentifier, t.identifier('__hash')), value));
}
var visitor = exports.visitor = {
ImportDeclaration: function ImportDeclaration(path, state) {
// import css from 'styled-jsx/css'
if (path.node.source.value !== 'styled-jsx/css') {
return;
}
// Find all the imported specifiers.
// e.g import css, { global, resolve } from 'styled-jsx/css'
// -> ['css', 'global', 'resolve']
var specifiersNames = path.node.specifiers.map(function (specifier) {
return specifier.local.name;
});
specifiersNames.forEach(function (tagName) {
// Get all the reference paths i.e. the places that use the tagName above
// eg.
// css`div { color: red }`
// css.global`div { color: red }`
// global`div { color: red `
var binding = path.scope.getBinding(tagName);
if (!binding || !Array.isArray(binding.referencePaths)) {
return;
}
// Produces an object containing all the TaggedTemplateExpression paths detected.
// The object contains { scoped, global, resolve }
var taggedTemplateExpressions = binding.referencePaths.map(function (ref) {
return ref.parentPath;
}).reduce(function (result, path) {
var taggedTemplateExpression = void 0;
if (path.isTaggedTemplateExpression()) {
// css`` global`` resolve``
taggedTemplateExpression = path;
} else if (path.parentPath && path.isMemberExpression() && path.parentPath.isTaggedTemplateExpression()) {
// This part is for css.global`` or css.resolve``
// using the default import css
taggedTemplateExpression = path.parentPath;
} else {
return result;
}
var tag = taggedTemplateExpression.get('tag');
var id = tag.isIdentifier() ? tag.node.name : tag.get('property').node.name;
if (result[id]) {
result[id].push(taggedTemplateExpression);
} else {
result.scoped.push(taggedTemplateExpression);
}
return result;
}, {
scoped: [],
global: [],
resolve: []
});
var hasJSXStyle = false;
var _state$opts = state.opts,
vendorPrefix = _state$opts.vendorPrefix,
sourceMaps = _state$opts.sourceMaps;
Object.keys(taggedTemplateExpressions).forEach(function (type) {
return taggedTemplateExpressions[type].forEach(function (path) {
hasJSXStyle = true;
// Process each css block
processTaggedTemplateExpression({
type: type,
path: path,
fileInfo: {
file: state.file,
sourceFileName: state.file.opts.sourceFileName,
sourceMaps: sourceMaps
},
splitRules: typeof state.opts.optimizeForSpeed === 'boolean' ? state.opts.optimizeForSpeed : process.env.NODE_ENV === 'production',
plugins: state.plugins,
vendorPrefix: vendorPrefix
});
});
});
// When using the `resolve` helper we need to add an import
// for the _JSXStyle component `styled-jsx/style`
if (hasJSXStyle && taggedTemplateExpressions.resolve.length > 0 && !state.hasInjectedJSXStyle && !path.scope.hasBinding(_constants.STYLE_COMPONENT)) {
state.hasInjectedJSXStyle = true;
var importDeclaration = (0, _utils.createReactComponentImportDeclaration)();
path.scope.path.node.body.unshift(importDeclaration);
}
});
// Finally remove the import
path.remove();
}
};