@stackbit/sdk
Version:
242 lines • 10 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getGatsbySourceFilesystemOptionsUsingRegExp = exports.getGatsbySourceFilesystemOptions = exports.extractNodeEnvironmentVariablesFromFile = exports.findDirsWithPackageDependency = void 0;
const path_1 = __importDefault(require("path"));
const lodash_1 = __importDefault(require("lodash"));
const utils_1 = require("@stackbit/utils");
// not sure why, but using import acorn from 'acorn' doesn't work
const acorn = require('acorn');
async function findDirsWithPackageDependency(fileBrowser, packageNames) {
const fileName = 'package.json';
const packageJsonExists = fileBrowser.fileNameExists(fileName);
if (!packageJsonExists) {
return [];
}
let filePaths = fileBrowser.getFilePathsForFileName(fileName);
filePaths = await (0, utils_1.reducePromise)(filePaths, async (filePaths, filePath) => {
const data = await fileBrowser.getFileData(filePath);
const hasDependency = lodash_1.default.some(packageNames, (packageName) => lodash_1.default.has(data, ['dependencies', packageName]));
const hasDevDependency = lodash_1.default.some(packageNames, (packageName) => lodash_1.default.has(data, ['devDependencies', packageName]));
if (hasDependency || hasDevDependency) {
filePaths.push(filePath);
}
return filePaths;
}, []);
return lodash_1.default.map(filePaths, (filePath) => path_1.default.parse(filePath).dir);
}
exports.findDirsWithPackageDependency = findDirsWithPackageDependency;
async function extractNodeEnvironmentVariablesFromFile(data) {
const envVars = [];
const envVarsRe = /process\.env\.(\w+)/g;
let reResult;
while ((reResult = envVarsRe.exec(data)) !== null) {
envVars.push(reResult[1]);
}
return lodash_1.default.uniq(envVars);
}
exports.extractNodeEnvironmentVariablesFromFile = extractNodeEnvironmentVariablesFromFile;
function getGatsbySourceFilesystemOptions(data) {
// Use https://astexplorer.net/ with "JavaScript" and "acorn" parser to generate ESTree
const ast = acorn.parse(data, { ecmaVersion: 2020 });
// find an object having the following format:
// {
// resolve: 'gatsby-source-filesystem',
// options: {
// path: `${__dirname}/content`,
// name: 'pages'
// }
// }
const result = [];
traverseESTree(ast, (node) => {
if (!isObjectExpressionNode(node)) {
return true;
}
const resolveProperty = findObjectProperty(node, 'resolve');
if (!resolveProperty) {
return true;
}
// we found an object with 'resolve' property, which is one of the plugins
// from now on, return false to not continue traversing the current subtree
const isGatsbySourceFileSystem = propertyValueEqual(resolveProperty, 'gatsby-source-filesystem');
if (!isGatsbySourceFileSystem) {
return false;
}
const optionsProperty = findObjectProperty(node, 'options');
if (!optionsProperty || !isObjectExpressionNode(optionsProperty.value)) {
return false;
}
const pathProperty = findObjectProperty(optionsProperty.value, 'path');
const nameProperty = findObjectProperty(optionsProperty.value, 'name');
if (!pathProperty || !nameProperty) {
return false;
}
let pathValue = getNodeValue(pathProperty.value);
const nameValue = getNodeValue(nameProperty.value);
if (typeof pathValue !== 'string' || typeof nameValue !== 'string') {
return false;
}
pathValue = pathValue.replace(/^\${__dirname}\//, '');
const ignoreProperty = findObjectProperty(optionsProperty.value, 'ignore');
const ignoreValue = ignoreProperty ? getNodeValue(ignoreProperty.value) : null;
result.push({
name: nameValue,
path: pathValue,
...(isStringArray(ignoreValue) ? { ignore: ignoreValue } : {})
});
}, {
iteratePrimitives: false
});
return result;
}
exports.getGatsbySourceFilesystemOptions = getGatsbySourceFilesystemOptions;
function findObjectProperty(node, propertyName) {
return lodash_1.default.find(node.properties, (property) => {
return isPropertyNode(property) && propertyNameEqual(property, propertyName);
});
}
function propertyNameEqual(property, propertyName) {
// check both identifier and literal properties
// { propertyName: '...' } OR { 'propertyName': '...' }
return (isIdentifierNode(property.key) && property.key.name === propertyName) || (isLiteralNode(property.key) && property.key.value === propertyName);
}
function propertyValueEqual(property, propertyValue) {
// check both literal and template literals values
// { propertyName: 'propertyValue' } OR { propertyName: `propertyValue` }
if (isLiteralNode(property.value) && property.value.value === propertyValue) {
return true;
}
if (isTemplateLiteralNode(property.value)) {
const value = property.value;
return (value.expressions.length === 0 &&
value.quasis.length === 1 &&
isTemplateElementNode(value.quasis[0]) &&
value.quasis[0].value.raw === propertyValue);
}
return false;
}
/**
* This method doesn't serialize every possible ESTree node value. It only
* serializes literals, template literals and array expressions needed to
* extract simple hard-coded values.
*
* If this method cannot serialize a value, it returns undefined
*/
function getNodeValue(node) {
if (isLiteralNode(node)) {
return node.value;
}
else if (isTemplateLiteralNode(node)) {
const expressions = node.expressions;
const quasis = node.quasis;
const sortedNodes = lodash_1.default.sortBy([...expressions, ...quasis], 'start');
return lodash_1.default.reduce(sortedNodes, (result, node) => {
if (result === undefined) {
return result;
}
if (isTemplateElementNode(node)) {
return result + node.value.raw;
}
else if (isIdentifierNode(node)) {
return result + '${' + node.name + '}';
}
return undefined;
}, '');
}
else if (isArrayExpressionNode(node)) {
return lodash_1.default.reduce(node.elements, (result, node) => {
if (result === undefined) {
return result;
}
if (node === null) {
return undefined;
}
const value = getNodeValue(node);
if (value === undefined) {
return value;
}
result.push(value);
return result;
}, []);
}
return undefined;
}
function isObjectExpressionNode(node) {
return node.type === 'ObjectExpression';
}
function isArrayExpressionNode(node) {
return node.type === 'ArrayExpression';
}
function isIdentifierNode(node) {
return node.type === 'Identifier';
}
function isLiteralNode(node) {
return node.type === 'Literal';
}
function isPropertyNode(node) {
return node.type === 'Property';
}
function isTemplateLiteralNode(node) {
return node.type === 'TemplateLiteral';
}
function isTemplateElementNode(node) {
return node.type === 'TemplateElement';
}
function isStringArray(value) {
return lodash_1.default.isArray(value) && lodash_1.default.every(value, lodash_1.default.isString);
}
function traverseESTree(value, iteratee, options = {}) {
const context = lodash_1.default.get(options, 'context');
const iterateCollections = lodash_1.default.get(options, 'iterateCollections', true);
const iteratePrimitives = lodash_1.default.get(options, 'iteratePrimitives', true);
function _traverse(value, keyPath, stack) {
const isArrayOrObject = lodash_1.default.isPlainObject(value) || lodash_1.default.isArray(value) || value instanceof acorn.Node;
const invokeIteratee = isArrayOrObject ? iterateCollections : iteratePrimitives;
let continueTraversing = true;
if (invokeIteratee) {
continueTraversing = iteratee.call(context, value, keyPath, stack);
}
if (isArrayOrObject && continueTraversing) {
lodash_1.default.forEach(value, (val, key) => {
_traverse(val, lodash_1.default.concat(keyPath, key), lodash_1.default.concat(stack, [value]));
});
}
}
_traverse(value, [], []);
}
function getGatsbySourceFilesystemOptionsUsingRegExp(data) {
// {
// resolve: `gatsby-source-filesystem`,
// options: {
// name: 'pages',
// path: `${__dirname}/src/pages`
// }
// }
// eslint-disable-next-line
const gatsbySourceFilesystemRegExp = /resolve\s*:\s*(['"`])gatsby-source-filesystem\1\s*,\s*options\s*:\s*{\s*(\w+)\s*:\s*(['"`])([^'"`]+)\3\s*,\s*(\w+)\s*:\s*(['"`])([^'"`]+)\6/g;
let match;
const fileSystemOptions = [];
while ((match = gatsbySourceFilesystemRegExp.exec(data)) !== null) {
const option1 = match[2];
const option2 = match[5];
const value1 = match[4];
const value2 = match[7];
if (option1 === 'name' && option2 === 'path' && value1 && value2) {
fileSystemOptions.push({
name: value1,
path: value2
});
}
else if (option1 === 'path' && option2 === 'name' && value1 && value2) {
fileSystemOptions.push({
name: value2,
path: value1
});
}
}
return fileSystemOptions;
}
exports.getGatsbySourceFilesystemOptionsUsingRegExp = getGatsbySourceFilesystemOptionsUsingRegExp;
//# sourceMappingURL=analyzer-utils.js.map