123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- 'use strict';
- var fs = require('fs');
- var path = require('path');
- var babylon = require('babylon');
- var t = require('babel-types');
- var generate = require('babel-generator').default;
- var traverse = require('babel-traverse').default;
- var resolve = require('resolve');
- var camelToDashed = require('../lib/parsers').camelToDashed;
- var basename = path.basename;
- var dirname = path.dirname;
- var uniqueIndex = 0;
- function getUniqueIndex() {
- return uniqueIndex++;
- }
- var property_files = fs
- .readdirSync(path.resolve(__dirname, '../lib/properties'))
- .filter(function(property) {
- return property.substr(-3) === '.js';
- });
- var out_file = fs.createWriteStream(path.resolve(__dirname, '../lib/properties.js'), {
- encoding: 'utf-8',
- });
- var date_today = new Date();
- out_file.write(
- "'use strict';\n\n// autogenerated - " +
- (date_today.getMonth() + 1 + '/' + date_today.getDate() + '/' + date_today.getFullYear()) +
- '\n\n'
- );
- out_file.write('/*\n *\n * https://www.w3.org/Style/CSS/all-properties.en.html\n */\n\n');
- function isModuleDotExports(node) {
- return (
- t.isMemberExpression(node, { computed: false }) &&
- t.isIdentifier(node.object, { name: 'module' }) &&
- t.isIdentifier(node.property, { name: 'exports' })
- );
- }
- function isRequire(node, filename) {
- if (
- t.isCallExpression(node) &&
- t.isIdentifier(node.callee, { name: 'require' }) &&
- node.arguments.length === 1 &&
- t.isStringLiteral(node.arguments[0])
- ) {
- var relative = node.arguments[0].value;
- var fullPath = resolve.sync(relative, { basedir: dirname(filename) });
- return { relative: relative, fullPath: fullPath };
- } else {
- return false;
- }
- }
- // step 1: parse all files and figure out their dependencies
- var parsedFilesByPath = {};
- property_files.map(function(property) {
- var filename = path.resolve(__dirname, '../lib/properties/' + property);
- var src = fs.readFileSync(filename, 'utf8');
- property = basename(property, '.js');
- var ast = babylon.parse(src);
- var dependencies = [];
- traverse(ast, {
- enter(path) {
- var r;
- if ((r = isRequire(path.node, filename))) {
- dependencies.push(r.fullPath);
- }
- },
- });
- parsedFilesByPath[filename] = {
- filename: filename,
- property: property,
- ast: ast,
- dependencies: dependencies,
- };
- });
- // step 2: serialize the files in an order where dependencies are always above
- // the files they depend on
- var externalDependencies = [];
- var parsedFiles = [];
- var addedFiles = {};
- function addFile(filename, dependencyPath) {
- if (dependencyPath.indexOf(filename) !== -1) {
- throw new Error(
- 'Circular dependency: ' +
- dependencyPath
- .slice(dependencyPath.indexOf(filename))
- .concat([filename])
- .join(' -> ')
- );
- }
- var file = parsedFilesByPath[filename];
- if (addedFiles[filename]) {
- return;
- }
- if (!file) {
- externalDependencies.push(filename);
- } else {
- file.dependencies.forEach(function(dependency) {
- addFile(dependency, dependencyPath.concat([filename]));
- });
- parsedFiles.push(parsedFilesByPath[filename]);
- }
- addedFiles[filename] = true;
- }
- Object.keys(parsedFilesByPath).forEach(function(filename) {
- addFile(filename, []);
- });
- // Step 3: add files to output
- // renaming exports to local variables `moduleName_export_exportName`
- // and updating require calls as appropriate
- var moduleExportsByPath = {};
- var statements = [];
- externalDependencies.forEach(function(filename, i) {
- var id = t.identifier(
- 'external_dependency_' + basename(filename, '.js').replace(/[^A-Za-z]/g, '') + '_' + i
- );
- moduleExportsByPath[filename] = { defaultExports: id };
- var relativePath = path.relative(path.resolve(__dirname + '/../lib'), filename);
- if (relativePath[0] !== '.') {
- relativePath = './' + relativePath;
- }
- statements.push(
- t.variableDeclaration('var', [
- t.variableDeclarator(
- id,
- t.callExpression(t.identifier('require'), [t.stringLiteral(relativePath)])
- ),
- ])
- );
- });
- function getRequireValue(node, file) {
- var r, e;
- // replace require("./foo").bar with the named export from foo
- if (
- t.isMemberExpression(node, { computed: false }) &&
- (r = isRequire(node.object, file.filename))
- ) {
- e = moduleExportsByPath[r.fullPath];
- if (!e) {
- return;
- }
- if (!e.namedExports) {
- return t.memberExpression(e.defaultExports, node.property);
- }
- if (!e.namedExports[node.property.name]) {
- throw new Error(r.relative + ' does not export ' + node.property.name);
- }
- return e.namedExports[node.property.name];
- // replace require("./foo") with the default export of foo
- } else if ((r = isRequire(node, file.filename))) {
- e = moduleExportsByPath[r.fullPath];
- if (!e) {
- if (/^\.\.\//.test(r.relative)) {
- node.arguments[0].value = r.relative.substr(1);
- }
- return;
- }
- return e.defaultExports;
- }
- }
- parsedFiles.forEach(function(file) {
- var namedExports = {};
- var localVariableMap = {};
- traverse(file.ast, {
- enter(path) {
- // replace require calls with the corresponding value
- var r;
- if ((r = getRequireValue(path.node, file))) {
- path.replaceWith(r);
- return;
- }
- // if we see `var foo = require('bar')` we can just inline the variable
- // representing `require('bar')` wherever `foo` was used.
- if (
- t.isVariableDeclaration(path.node) &&
- path.node.declarations.length === 1 &&
- t.isIdentifier(path.node.declarations[0].id) &&
- (r = getRequireValue(path.node.declarations[0].init, file))
- ) {
- var newName = 'compiled_local_variable_reference_' + getUniqueIndex();
- path.scope.rename(path.node.declarations[0].id.name, newName);
- localVariableMap[newName] = r;
- path.remove();
- return;
- }
- // rename all top level functions to keep them local to the module
- if (t.isFunctionDeclaration(path.node) && t.isProgram(path.parent)) {
- path.scope.rename(path.node.id.name, file.property + '_local_fn_' + path.node.id.name);
- return;
- }
- // rename all top level variables to keep them local to the module
- if (t.isVariableDeclaration(path.node) && t.isProgram(path.parent)) {
- path.node.declarations.forEach(function(declaration) {
- path.scope.rename(
- declaration.id.name,
- file.property + '_local_var_' + declaration.id.name
- );
- });
- return;
- }
- // replace module.exports.bar with a variable for the named export
- if (
- t.isMemberExpression(path.node, { computed: false }) &&
- isModuleDotExports(path.node.object)
- ) {
- var name = path.node.property.name;
- var identifier = t.identifier(file.property + '_export_' + name);
- path.replaceWith(identifier);
- namedExports[name] = identifier;
- }
- },
- });
- traverse(file.ast, {
- enter(path) {
- if (
- t.isIdentifier(path.node) &&
- Object.prototype.hasOwnProperty.call(localVariableMap, path.node.name)
- ) {
- path.replaceWith(localVariableMap[path.node.name]);
- }
- },
- });
- var defaultExports = t.objectExpression(
- Object.keys(namedExports).map(function(name) {
- return t.objectProperty(t.identifier(name), namedExports[name]);
- })
- );
- moduleExportsByPath[file.filename] = {
- namedExports: namedExports,
- defaultExports: defaultExports,
- };
- statements.push(
- t.variableDeclaration(
- 'var',
- Object.keys(namedExports).map(function(name) {
- return t.variableDeclarator(namedExports[name]);
- })
- )
- );
- statements.push.apply(statements, file.ast.program.body);
- });
- var propertyDefinitions = [];
- parsedFiles.forEach(function(file) {
- var dashed = camelToDashed(file.property);
- propertyDefinitions.push(
- t.objectProperty(
- t.identifier(file.property),
- t.identifier(file.property + '_export_definition')
- )
- );
- if (file.property !== dashed) {
- propertyDefinitions.push(
- t.objectProperty(t.stringLiteral(dashed), t.identifier(file.property + '_export_definition'))
- );
- }
- });
- var definePropertiesCall = t.callExpression(
- t.memberExpression(t.identifier('Object'), t.identifier('defineProperties')),
- [t.identifier('prototype'), t.objectExpression(propertyDefinitions)]
- );
- statements.push(
- t.expressionStatement(
- t.assignmentExpression(
- '=',
- t.memberExpression(t.identifier('module'), t.identifier('exports')),
- t.functionExpression(
- null,
- [t.identifier('prototype')],
- t.blockStatement([t.expressionStatement(definePropertiesCall)])
- )
- )
- )
- );
- out_file.write(generate(t.program(statements)).code + '\n');
- out_file.end(function(err) {
- if (err) {
- throw err;
- }
- });
|