diff --git a/package.json b/package.json index 99a42ffd282..eae5eacff0f 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "recast": "^0.12.3", "resolve": "^1.3.2", "sass-loader": "^6.0.4", + "scss-parser": "^1.0.0", "semver": "^5.3.0", "standard-changelog": "0.0.1", "style-loader": "^0.18.0", @@ -86,6 +87,7 @@ "stylelint-selector-bem-pattern": "^1.0.0", "testdouble": "3.0.0", "to-slug-case": "^1.0.0", + "query-ast": "^1.0.1", "validate-commit-msg": "^2.6.1", "webpack": "^2.2.1", "webpack-dev-server": "^2.4.3" diff --git a/scripts/rewrite-sass-import-statements-for-closure.js b/scripts/rewrite-sass-import-statements-for-closure.js new file mode 100644 index 00000000000..d9d32ff9403 --- /dev/null +++ b/scripts/rewrite-sass-import-statements-for-closure.js @@ -0,0 +1,89 @@ +/** + * Copyright 2017 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @fileoverview Rewrites import statements such that: + * + * ```js + * import [ from] '@material/$PKG[/files...]'; + * ``` + * becomes + * ```js + * import [ from] 'mdc-$PKG/'; + * ``` + * The RESOLVED_FILE_PATH is the file that node's module resolution algorithm would have resolved the import + * source to. + */ + +const fs = require('fs'); +const path = require('path'); + +const {parse, stringify} = require('scss-parser'); +const createQueryWrapper = require('query-ast'); +const glob = require('glob'); + +main(process.argv); + +function main(argv) { + if (argv.length < 3) { + console.error('Missing root directory path'); + process.exit(1); + } + + const rootDir = path.resolve(process.argv[2]); + const srcFiles = glob.sync(`${rootDir}/**/*.scss`); + srcFiles.forEach((srcFile) => transform(srcFile, rootDir)); +} + +function transform(srcFile, rootDir) { + const src = fs.readFileSync(srcFile, 'utf8'); + const ast = parse(src); + + const $ = createQueryWrapper(ast); + $('atrule').has('atkeyword').find('string_double').replace((n) => { + if (n.parent.children[0].node.value === 'import') { + return { + type: 'string_double', + value: rewriteImportDeclaration(n.node.value, srcFile, rootDir), + }; + } + return n.node; + }); + + const scss = stringify($().get(0)); + + fs.writeFileSync(srcFile, scss, 'utf8'); + console.log(`[rewrite] ${srcFile}`); +} + +function rewriteImportDeclaration(importSource, srcFile, rootDir) { + const pathParts = importSource.split('/'); + const isMDCImport = pathParts[0] === '@material'; + if (isMDCImport) { + const modName = pathParts[1]; // @material/ + const atMaterialReplacementPath = `${rootDir}/${modName}`; + const rewrittenImportSource = [atMaterialReplacementPath].concat(pathParts.slice(2)).join('/'); + importSource = rewrittenImportSource; + } + + let resolvedImportSource = importSource; + const needsClosureModuleRootResolution = path.isAbsolute(importSource); + if (needsClosureModuleRootResolution) { + const pathToImport = importSource.replace('@material', rootDir); + resolvedImportSource = path.relative(path.dirname(srcFile), pathToImport); + } + return resolvedImportSource; +} diff --git a/scripts/sass-closure-rewriter.sh b/scripts/sass-closure-rewriter.sh new file mode 100755 index 00000000000..99786d5aa6c --- /dev/null +++ b/scripts/sass-closure-rewriter.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +# Rewrites our .scss files to be compatible with closure-stylesheets +# in our internal Blaze infrastructure. + +## +# Copyright 2017 Google Inc. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +set -e + +function log() { + echo -e "\033[36m[closure-rewriter]\033[0m" "$@" +} + +CLOSURE_TMP=.closure-tmp +CLOSURE_PKGDIR=$CLOSURE_TMP/packages + +log "Prepping packages for rewrite" + +rm -fr $CLOSURE_TMP/** +mkdir -p $CLOSURE_PKGDIR +PACKAGE_NAMES=$(ls packages) +for pkg in $PACKAGE_NAMES ; do + if [[ $pkg != *"mdc-"* ]]; then + continue + fi + cp -r "packages/$pkg" $CLOSURE_PKGDIR +done +rm -fr $CLOSURE_PKGDIR/**/{node_modules,dist} + +log "Rewriting all import statements to be closure compatible" +node scripts/rewrite-sass-import-statements-for-closure.js $CLOSURE_PKGDIR