material-components_materia.../scripts/check-pkg-for-release.js
Steven Orvell 286d4733ce [first commit] lots of wip elements
These are WIP. Some known issues:
* select, chips, and textfield ripple not working
* many tests to be added
* some elements tbd, including e.g. top-app-bar and drawer
2018-04-25 19:20:55 -07:00

240 lines
8.8 KiB
JavaScript

/**
* 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.
*/
/**
* @fileoverview Used within pre-release.sh, this checks a component's package.json
* to ensure that if it's a new component (version = "0.0.0"), it will have a proper
* "publishConfig.access" property set to "public".
* 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]));
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', 'animation', 'auto-init', 'rtl', 'selection-control'];
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 = getPkgName();
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 === 'main-js-a-la-carte';
});
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 = getPkgName();
if (CSS_WHITELIST.indexOf(name) === -1) {
const cssconfig = WEBPACK_CONFIG.find((value) => {
return value.name === 'main-css-a-la-carte';
});
const nameMDC = pkg.name.replace('@material/', 'mdc.');
assert.notEqual(typeof cssconfig.entry[nameMDC], 'undefined',
'FAILURE: Component ' + pkg.name + ' css dependency 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 = getPkgName();
const nameMDC = `mdc-${name}`;
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', 'selection-control'];
const name = getPkgName();
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 + '/index') {
isImported = true;
}
}
},
});
return isImported;
}
function checkAutoInitAddedInMDCPackage(ast) {
let nameCamel = camelCase(pkg.name.replace('@material/', ''));
if (nameCamel === 'textfield') {
nameCamel = 'textField';
}
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;
}
function checkComponentExportedAddedInMDCPackage(ast) {
let nameCamel = camelCase(pkg.name.replace('@material/', ''));
if (nameCamel === 'textfield') {
nameCamel = 'textField';
}
let isExported = false;
traverse(ast, {
'ExportNamedDeclaration'({node}) {
if (node.specifiers) {
if (node.specifiers.find((value) => {
return value.exported.name === nameCamel;
})) {
isExported = true;
}
}
},
});
return isExported;
}
function getPkgName() {
let name = pkg.name.split('/')[1];
if (name === 'textfield') {
// Text-field now has a dash in the name. The package cannot be changed,
// since it is a lot of effort to rename npm package
name = 'text-field';
}
return name;
}