Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Commit

Permalink
chore(infrastructure): Test all components are property imported and …
Browse files Browse the repository at this point in the history
…have necessary configs (#916)

Close #811
  • Loading branch information
yeelan0319 authored Jul 13, 2017
1 parent 9e6623e commit 90d21aa
Show file tree
Hide file tree
Showing 7 changed files with 245 additions and 30 deletions.
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@
"lint": "npm-run-all --parallel lint:*",
"postinstall": "lerna bootstrap",
"pretest": "npm run lint && node scripts/check-imports.js",
"test": "npm run test:unit && npm run test:closure",
"test": "npm run test:unit && npm run test:closure && npm run test:dependency",
"posttest": "istanbul report --root coverage text-summary && istanbul check-coverage --lines 95 --statements 95 --branches 95 --functions 95",
"test:watch": "karma start --auto-watch",
"test:unit": "karma start --single-run",
"test:closure": "./scripts/closure-test.sh"
"test:closure": "./scripts/closure-test.sh",
"test:dependency": "./scripts/dependency-test.sh"
},
"devDependencies": {
"ascii-table": "0.0.9",
Expand All @@ -42,6 +43,7 @@
"cp-file": "^4.1.0",
"cross-env": "^5.0.0",
"css-loader": "^0.28.0",
"cssom": "^0.3.2",
"cz-conventional-changelog": "^2.0.0",
"del-cli": "^1.0.0",
"dom-events": "^0.1.1",
Expand Down Expand Up @@ -71,6 +73,7 @@
"node-sass": "^4.0.0",
"npm-run-all": "^3.1.2",
"postcss-loader": "^2.0.3",
"query-ast": "^1.0.1",
"raw-loader": "^0.5.1",
"recast": "^0.12.3",
"resolve": "^1.3.2",
Expand All @@ -87,7 +90,6 @@
"stylelint-selector-bem-pattern": "^1.0.0",
"testdouble": "3.2.0",
"to-slug-case": "^1.0.0",
"query-ast": "^1.0.1",
"validate-commit-msg": "^2.6.1",
"webpack": "^3.0.0",
"webpack-dev-server": "^2.4.3"
Expand Down
22 changes: 11 additions & 11 deletions packages/material-components-web/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,24 @@
* limitations under the License.
*/

import autoInit from '@material/auto-init';
import * as base from '@material/base';
import * as checkbox from '@material/checkbox';
import * as dialog from '@material/dialog';
import * as drawer from '@material/drawer';
import * as formField from '@material/form-field';
import * as gridList from '@material/grid-list';
import * as iconToggle from '@material/icon-toggle';
import * as radio from '@material/radio';
import * as ripple from '@material/ripple';
import * as dialog from '@material/dialog';
import * as drawer from '@material/drawer';
import * as textfield from '@material/textfield';
import * as snackbar from '@material/snackbar';
import * as linearProgress from '@material/linear-progress';
import * as menu from '@material/menu';
import * as radio from '@material/radio';
import * as ripple from '@material/ripple';
import * as select from '@material/select';
import * as slider from '@material/slider';
import * as snackbar from '@material/snackbar';
import * as tabs from '@material/tabs';
import * as textfield from '@material/textfield';
import * as toolbar from '@material/toolbar';
import autoInit from '@material/auto-init';

// Register all components
autoInit.register('MDCCheckbox', checkbox.MDCCheckbox);
Expand All @@ -54,6 +54,7 @@ autoInit.register('MDCToolbar', toolbar.MDCToolbar);

// Export all components.
export {
autoInit,
base,
checkbox,
dialog,
Expand All @@ -62,14 +63,13 @@ export {
gridList,
iconToggle,
linearProgress,
menu,
radio,
ripple,
select,
slider,
snackbar,
tabs,
textfield,
menu,
select,
slider,
toolbar,
autoInit,
};
1 change: 1 addition & 0 deletions packages/material-components-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"@material/menu": "^0.4.1",
"@material/radio": "^0.2.7",
"@material/ripple": "^0.8.0",
"@material/rtl": "^0.1.6",
"@material/select": "^0.3.10",
"@material/slider": "^0.2.0",
"@material/snackbar": "^0.3.0",
Expand Down
1 change: 0 additions & 1 deletion packages/mdc-elevation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
"material design",
"elevation"
],
"main": "index.js",
"repository": {
"type": "git",
"url": "https://github.com/material-components/material-components-web.git"
Expand Down
200 changes: 195 additions & 5 deletions scripts/check-pkg-for-release.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,203 @@
* The argument should be the package.json file to check.
*/

const assert = require('assert');
const fs = require('fs');
const path = require('path');

const {default: traverse} = require('babel-traverse');
const babylon = require('babylon');
const camelCase = require('camel-case');
const cssom = require('CSSOM');
const recast = require('recast');

const pkg = require(path.join(process.env.PWD, process.argv[process.argv.length - 1]));
if (pkg.version !== '0.0.0') {
process.exit(0);

const REPO_PKG = require(path.join(process.env.PWD, 'package.json'));
const WEBPACK_CONFIG_PATH = 'webpack.config.js';
const WEBPACK_CONFIG = require(path.join(process.env.PWD, WEBPACK_CONFIG_PATH));
const MASTER_PKG_PATH = 'packages/material-components-web/package.json';
const MASTER_CSS_PATH = 'packages/material-components-web/material-components-web.scss';
const MASTER_JS_PATH = 'packages/material-components-web/index.js';
const MASTER_PKG = require(path.join(process.env.PWD, MASTER_PKG_PATH));
// These few MDC packages work as foundation or utility packages, and are not
// directly included in webpack or the material-component-web module. But they
// are necessary since other MDC packages depend on them.
const CSS_WHITELIST = ['base', 'auto-init', 'rtl'];

main();

function main() {
checkPublicConfigForNewComponent();
if (pkg.name !== MASTER_PKG.name) {
checkNameIsPresentInAllowedScope();
checkDependencyAddedInWebpackConfig();
checkDependencyAddedInMDCPackage();
}
}

function checkPublicConfigForNewComponent() {
if (pkg.version === '0.0.0') {
assert.notEqual(typeof pkg.publishConfig, 'undefined',
'Please add publishConfig to' + pkg.name + '\'s package.json. Consult our ' +
'docs/authoring-components.md to ensure your component\'s package.json ' +
'is well-formed.');
assert.equal(pkg.publishConfig.access, 'public',
'Please set publishConfig.access to "public" in ' + pkg.name + '\'s package.json. ' +
'Consult our docs/authoring-components.md to ensure your component\'s package.json ' +
'is well-formed.');
}
}

function checkNameIsPresentInAllowedScope() {
const name = pkg.name.split('/')[1];
assert.notEqual(REPO_PKG.config['validate-commit-msg']['scope']['allowed'].indexOf(name), -1,
'FAILURE: Component ' + pkg.name + ' is not added to allowed scope. Please check package.json ' +
'and add ' + name + ' to config["validate-commit-msg"]["scope"]["allowed"] before commit.');
}

function checkDependencyAddedInWebpackConfig() {
// Check if css has been added to webpack config
checkCSSDependencyAddedInWebpackConfig();

// Check if js component has been added to webpack config
if (typeof(pkg.main) !== 'undefined') {
checkJSDependencyAddedInWebpackConfig();
}
}

function checkJSDependencyAddedInWebpackConfig() {
const jsconfig = WEBPACK_CONFIG.find((value) => {
return value.name === 'js-components';
});
const nameCamel = camelCase(pkg.name.replace('@material/', ''));
assert.notEqual(typeof jsconfig.entry[nameCamel], 'undefined',
'FAILURE: Component ' + pkg.name + ' javascript dependency is not added to webpack ' +
'configuration. Please add ' + nameCamel + ' to ' + WEBPACK_CONFIG_PATH + '\'s js-components ' +
'entry before commit.');
}

function checkCSSDependencyAddedInWebpackConfig() {
const name = pkg.name.split('/')[1];
if (CSS_WHITELIST.indexOf(name) === -1) {
const cssconfig = WEBPACK_CONFIG.find((value) => {
return value.name === 'css';
});
const nameMDC = pkg.name.replace('@material/', 'mdc.');
assert.notEqual(typeof cssconfig.entry[nameMDC], 'undefined',
'FAILURE: Component ' + pkg.name + ' css denpendency not added to webpack ' +
'configuration. Please add ' + name + ' to ' + WEBPACK_CONFIG_PATH + '\'s css ' +
'entry before commit.');
}
}

function checkDependencyAddedInMDCPackage() {
// Package is added to package.json
checkPkgDependencyAddedInMDCPackage();

// SCSS is added to @import rule
checkCSSDependencyAddedInMDCPackage();

// If any, foundation is added to index and autoInit
checkJSDependencyAddedInMDCPackage();
}

function checkPkgDependencyAddedInMDCPackage() {
assert.notEqual(typeof MASTER_PKG.dependencies[pkg.name], 'undefined',
'FAILURE: Component ' + pkg.name + ' is not a denpendency for MDC-web. ' +
'Please add ' + pkg.name +' to ' + MASTER_PKG_PATH + '\' dependencies before commit.');
}

function checkCSSDependencyAddedInMDCPackage() {
const name = pkg.name.split('/')[1];
const nameMDC = pkg.name.replace('@material/', 'mdc-');
if (CSS_WHITELIST.indexOf(name) === -1) {
const src = fs.readFileSync(path.join(process.env.PWD, MASTER_CSS_PATH), 'utf8');
const cssRules = cssom.parse(src).cssRules;
const cssRule = path.join(pkg.name, nameMDC);

assert.notEqual(typeof cssRules.find((value) => {
return value.href === cssRule;
}), 'undefined',
'FAILURE: Component ' + pkg.name + ' is not being imported in MDC-web. ' +
'Please add ' + name + ' to ' + MASTER_CSS_PATH + ' import rule before commit.');
}
}

function checkJSDependencyAddedInMDCPackage() {
const NOT_IMPORTED = ['animation'];
const NOT_AUTOINIT = ['auto-init', 'base', 'form-field'];
const name = pkg.name.split('/')[1];
if (typeof(pkg.main) !== 'undefined' && NOT_IMPORTED.indexOf(name) === -1) {
const nameCamel = camelCase(pkg.name.replace('@material/', ''));
const src = fs.readFileSync(path.join(process.env.PWD, MASTER_JS_PATH), 'utf8');
const ast = recast.parse(src, {
parser: {
parse: (code) => babylon.parse(code, {sourceType: 'module'}),
},
});
assert(checkComponentImportedAddedInMDCPackage(ast), 'FAILURE: Component ' +
pkg.name + ' is not being imported in MDC-web. ' + 'Please add ' + nameCamel +
' to '+ MASTER_JS_PATH + ' import rule before commit.');
assert(checkComponentExportedAddedInMDCPackage(ast), 'FAILURE: Component ' +
pkg.name + ' is not being exported in MDC-web. ' + 'Please add ' + nameCamel +
' to '+ MASTER_JS_PATH + ' export before commit.');
if (NOT_AUTOINIT.indexOf(name) === -1) {
assert(checkAutoInitAddedInMDCPackage(ast) > 0, 'FAILURE: Component ' +
pkg.name + ' seems not being auto inited in MDC-web. ' + 'Please add ' +
nameCamel + ' to '+ MASTER_JS_PATH + ' autoInit statement before commit.');
}
}
}

function checkComponentImportedAddedInMDCPackage(ast) {
let isImported = false;
traverse(ast, {
'ImportDeclaration'({node}) {
if (node.source) {
const source = node.source.value;
if (source === pkg.name) {
isImported = true;
}
}
},
});
return isImported;
}

function checkAutoInitAddedInMDCPackage(ast) {
const nameCamel = camelCase(pkg.name.replace('@material/', ''));
let autoInitedCount = 0;
traverse(ast, {
'ExpressionStatement'({node}) {
const callee = node.expression.callee;
const args = node.expression.arguments;
if (callee.object.name === 'autoInit' && callee.property.name === 'register') {
const expression = args.find((value) => {
return value.type === 'MemberExpression';
});
if (expression.object.name === nameCamel) {
autoInitedCount++;
}
}
},
});
return autoInitedCount;
}

const missingPublishConfig = !pkg.publishConfig;
if (missingPublishConfig || pkg.publishConfig.access !== 'public') {
process.exit(1);
function checkComponentExportedAddedInMDCPackage(ast) {
const nameCamel = camelCase(pkg.name.replace('@material/', ''));
let isExported = false;
traverse(ast, {
'ExportNamedDeclaration'({node}) {
if (node.specifiers) {
if (node.specifiers.find((value) => {
return value.exported.name === nameCamel;
})) {
isExported = true;
}
}
},
});
return isExported;
}
31 changes: 31 additions & 0 deletions scripts/dependency-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#!/bin/sh

##
# Copyright 2016 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 '\033[36m[dependency-test]\033[0m' "$@"
}

for f in $(find packages -name 'package.json' -not -path "*/node_modules/*"); do
log "\tChecking dependency in $f"
node scripts/check-pkg-for-release.js "$f"
done
echo ""
echo "Dependency check passed."
echo ""
12 changes: 2 additions & 10 deletions scripts/pre-release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,8 @@ if ! gcloud config get-value project 2>/dev/null | grep -q material-components-w
exit 1
fi

log "Checking that all packages have correct accessConfig rules within their package.json files..."
for f in $(find packages -name 'package.json' -not -path "*/node_modules/*"); do
log "\tChecking $f"
if ! node scripts/check-pkg-for-release.js "$f"; then
echo "FAILURE: Did not find publishConfig.access: 'public' in $f. Please consult our" \
"docs/authoring-components.md file and ensure that the new component's package.json"\
"is well-formed."
exit 1
fi
done
log "Checking that all packages have correct dependency rules..."
sh ./scripts/dependency-test.sh

log "Running npm test to ensure no breakages..."
npm test
Expand Down

0 comments on commit 90d21aa

Please sign in to comment.