mirror of
https://github.com/material-components/material-web.git
synced 2026-03-09 00:09:23 +08:00
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
240 lines
8.8 KiB
JavaScript
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;
|
|
}
|