Skip to content

Commit

Permalink
🏗 Collect z-index from JS files
Browse files Browse the repository at this point in the history
  • Loading branch information
alanorozco committed Feb 24, 2021
1 parent bf0e1f3 commit 43d7f02
Show file tree
Hide file tree
Showing 5 changed files with 392 additions and 154 deletions.
44 changes: 33 additions & 11 deletions build-system/tasks/get-zindex/get-zindex.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@
*/
'use strict';

const m = require('.');
const test = require('ava');
const {getZindexSelectors, getZindexChainsInJs, createTable} = require('.');

const result = {
const cssResult = {
'test.css': {
'.selector-1': '1',
'.selector-2': '0',
Expand All @@ -31,21 +31,43 @@ const result = {
},
};

const jsResult = {
'test-0.js': {
'<div />': 'initial',
'assignment': 'auto',
'declarator': 0,
'setStyles': 9999,
},
'test-1.js': {
'<Component />': 12345,
},
};

test('collects selectors', async (t) => {
const data = await m.getZindexSelectors('*.css', __dirname);
t.deepEqual(data, result);
const data = await getZindexSelectors('*.css', __dirname);
t.deepEqual(data, cssResult);
});

test('collects chains from js', async (t) => {
const data = await getZindexChainsInJs('*.js', __dirname);
t.deepEqual(data, jsResult);
});

test('sync - create array of arrays with z index order', (t) => {
t.plan(1);
const table = m.createTable(result);
const table = createTable({...cssResult, ...jsResult});
const expected = [
['.selector-6', 'auto', 'test-2.css'],
['.selector-5', 'initial', 'test-2.css'],
['.selector-3', '99', 'test.css'],
['.selector-4', '80', 'test-2.css'],
['.selector-1', '1', 'test.css'],
['.selector-2', '0', 'test.css'],
['`assignment`', 'auto', '[test-0.js](/test-0.js)'],
['`.selector-6`', 'auto', '[test-2.css](/test-2.css)'],
['`<div />`', 'initial', '[test-0.js](/test-0.js)'],
['`.selector-5`', 'initial', '[test-2.css](/test-2.css)'],
['`<Component />`', 12345, '[test-1.js](/test-1.js)'],
['`setStyles`', 9999, '[test-0.js](/test-0.js)'],
['`.selector-3`', '99', '[test.css](/test.css)'],
['`.selector-4`', '80', '[test-2.css](/test-2.css)'],
['`.selector-1`', '1', '[test.css](/test.css)'],
['`declarator`', 0, '[test-0.js](/test-0.js)'],
['`.selector-2`', '0', '[test.css](/test.css)'],
];
t.deepEqual(table, expected);
});
57 changes: 53 additions & 4 deletions build-system/tasks/get-zindex/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ const globby = require('globby');
const path = require('path');
const postcss = require('postcss');
const prettier = require('prettier');
const tempy = require('tempy');
const textTable = require('text-table');
const {getStdout} = require('../../common/process');
const {readJsonSync} = require('fs-extra');
const {writeDiffOrFail} = require('../../common/diff');

const tableHeaders = [
['selector', 'z-index', 'file'],
['context', 'z-index', 'file'],
['---', '---', '---'],
];

Expand Down Expand Up @@ -71,7 +74,11 @@ function createTable(filesData) {
.sort()
.forEach((selectorName) => {
const zIndex = selectors[selectorName];
const row = [selectorName, zIndex, fileName];
const row = [
`\`${selectorName}\``,
zIndex,
`[${fileName}](/${fileName})`,
];
rows.push(row);
});
});
Expand All @@ -98,7 +105,7 @@ function createTable(filesData) {
/**
* Extract z-index selectors from all files matching the given glob starting at
* the given working directory
* @param {string} glob
* @param {string|Array<string>} glob
* @param {string=} cwd
* @return {Object}
*/
Expand All @@ -115,11 +122,52 @@ async function getZindexSelectors(glob, cwd = '.') {
return filesData;
}

/**
* @param {string|Array<string>} glob
* @param {string=} cwd
* @return {Object}
*/
async function getZindexChainsInJs(glob, cwd = '.') {
const files = globby.sync(glob, {cwd}).map((file) => path.join(cwd, file));
const filesIncludingString = getStdout(
['grep -irl "z-*index"', ...files].join(' ')
)
.trim()
.split('\n');
return tempy.write.task('{}', (temporary) => {
getStdout(
[
'npx jscodeshift',
'--dry',
'--parser babylon',
`--parser-config ${__dirname}/jscodeshift/parser-config.json`,
`--transform ${__dirname}/jscodeshift/collect-zindex.js`,
`--collectZindexToFile=${temporary}`,
...filesIncludingString,
].join(' ')
);
const resultAbsolute = readJsonSync(temporary);
const result = {};
for (const key in resultAbsolute) {
const relative = path.relative(cwd, key);
result[relative] = resultAbsolute[key];
}
return result;
});
}

/**
* Entry point for gulp get-zindex
*/
async function getZindex() {
const filesData = await getZindexSelectors('{css,src,extensions}/**/*.css');
const filesData = {
...(await getZindexSelectors('{css,src,extensions}/**/*.css')),
...(await getZindexChainsInJs([
'{3p,src,extensions}/**/*.js',
'!extensions/**/test/**/*.js',
'!extensions/**/storybook/**/*.js',
])),
};
const filename = 'css/Z_INDEX.md';
const rows = [...tableHeaders, ...createTable(filesData)];
const table = textTable(rows, tableOptions);
Expand Down Expand Up @@ -149,6 +197,7 @@ module.exports = {
createTable,
getZindex,
getZindexSelectors,
getZindexChainsInJs,
};

getZindex.description =
Expand Down
118 changes: 118 additions & 0 deletions build-system/tasks/get-zindex/jscodeshift/collect-zindex.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/**
* Copyright 2021 The AMP HTML Authors. 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.
*/
import {readJsonSync, writeJsonSync} from 'fs-extra';

const zIndexRegExp = /^z-?index$/i;

function getCallExpressionZIndexValue(node) {
for (let i = 0; i < node.arguments.length; i++) {
const argument = node.arguments[i];
const next = node.arguments[i + 1];
if (next && next.type === 'Literal' && zIndexRegExp.test(argument.value)) {
return next.value;
}
}
}

function source(file, node) {
const {start, end} = node;
return file.source.substr(start, end - start);
}

function chainId(file, path) {
let propertyChain = '';
let at = path;

while (at && at.value && at.value.type.endsWith('Property')) {
const part = source(file, at.value.key);
if (at.value.key.type === 'Identifier') {
propertyChain = `.${part}` + propertyChain;
} else {
propertyChain = `[${part}]` + propertyChain;
}
at = at.parent && at.parent.parent;
}

while (
at &&
at.parent &&
at.value.type !== 'CallExpression' &&
at.value.type !== 'VariableDeclarator' &&
at.value.type !== 'AssignmentExpression' &&
at.value.type !== 'JSXAttribute' &&
at.value.type !== 'Program'
) {
at = at.parent;
}

if (at.value.type === 'JSXAttribute') {
const openingElement = source(file, at.parent.value.name);
return `<${openingElement} />`;
}

if (at.value.type === 'CallExpression') {
return source(file, at.value.callee);
}

if (at.value.type === 'AssignmentExpression') {
return source(file, at.value.left) + propertyChain;
}

if (at.value.type === 'VariableDeclarator') {
return source(file, at.value.id) + propertyChain;
}

return '(unknown)';
}

export default function transformer(file, api, options) {
const j = api.jscodeshift;

const reports = {};

const {collectZindexToFile} = options;

j(file.source)
.find(
j.ObjectProperty,
(node) =>
node.key &&
node.value.type.endsWith('Literal') &&
node.value.value !== '' &&
zIndexRegExp.test(node.key.value || node.key.name)
)
.forEach((path) => {
reports[chainId(file, path.parent.parent)] = path.value.value.value;
});

j(file.source)
.find(
j.CallExpression,
(node) => getCallExpressionZIndexValue(node) != null
)
.forEach((path) => {
reports[chainId(file, path)] = getCallExpressionZIndexValue(path.value);
});

if (collectZindexToFile && Object.keys(reports).length > 0) {
writeJsonSync(collectZindexToFile, {
...readJsonSync(collectZindexToFile, {throws: false}),
[file.path]: reports,
});
}

return file.source;
}
33 changes: 33 additions & 0 deletions build-system/tasks/get-zindex/jscodeshift/parser-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"sourceType": "module",
"allowImportExportEverywhere": true,
"allowReturnOutsideFunction": true,
"startLine": 1,
"tokens": true,
"plugins": [
["flow", {"all": true}],
"flowComments",
"jsx",
"asyncGenerators",
"bigInt",
"classProperties",
"classPrivateProperties",
"classPrivateMethods",
["decorators", {"decoratorsBeforeExport": false}],
"doExpressions",
"dynamicImport",
"exportDefaultFrom",
"exportNamespaceFrom",
"functionBind",
"functionSent",
"importMeta",
"logicalAssignment",
"nullishCoalescingOperator",
"numericSeparator",
"objectRestSpread",
"optionalCatchBinding",
"optionalChaining",
["pipelineOperator", {"proposal": "minimal"}],
"throwExpressions"
]
}
Loading

0 comments on commit 43d7f02

Please sign in to comment.