@babel/core#NodePath TypeScript Examples

The following examples show how to use @babel/core#NodePath. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: separateVariableDeclarations.ts    From vidact with MIT License 6 votes vote down vote up
export function separateVariableDeclarations(fnPath: NodePath) {
  fnPath.traverse({
    VariableDeclaration(path) {
      const declarations = path.get("declarations");
      if (declarations.length < 2) {
        return;
      }

      path.replaceWithMultiple(
        declarations.map(declaration =>
          t.variableDeclaration(path.get("kind"), [declaration.node])
        )
      );
    }
  });
}
Example #2
Source File: typecheck.macro.ts    From typecheck.macro with MIT License 6 votes vote down vote up
function finalizeType(
  path: NodePath<t.Node>,
  instantiatedTypes: Map<string, TypeInfo>,
  namedTypes: Map<string, IR>
): [IR, Map<string, number>] {
  const typeParam = getTypeParameter(path);
  let ir = getTypeParameterIR(typeParam.node);
  const state: InstantiationStatePartial = {
    instantiatedTypes,
    namedTypes,
    typeStats: new Map(),
    newInstantiatedTypes: [],
  };
  // no type resolution on the type parameter
  ir = flattenType(ir);
  const instantiatedIR = instantiateIR(ir, state);
  for (const type of state.newInstantiatedTypes) {
    const newType = instantiatedTypes.get(type);
    if (newType === undefined) {
      throwUnexpectedError(`did not expected ${type} to be undefined`);
    }
    newType.value = cleanUnions(
      solveIntersections(newType.value, instantiatedTypes),
      instantiatedTypes
    );
    instantiatedTypes.set(type, newType);
  }
  const finalIR = cleanUnions(
    solveIntersections(instantiatedIR, instantiatedTypes),
    instantiatedTypes
  );
  return [finalIR, state.typeStats];
}
Example #3
Source File: declarationToAssignment.ts    From vidact with MIT License 6 votes vote down vote up
export function declarationToAssignment(path: NodePath<t.VariableDeclaration>) {
  const declarator = path.get("declarations")[0];
  const { id, init } = declarator.node;
  path.replaceWith(
    t.expressionStatement(
      t.assignmentExpression("=", id, init || t.identifier("undefined"))
    )
  );

  let ids: string[];
  if (t.isIdentifier(id)) {
    ids = [id.name];
  } else {
    declarator.get("id").traverse(
      {
        Identifier(idPath, state) {
          const { name } = idPath.node;
          if (!state.includes(name)) {
            state.push(name);
          }
        }
      },
      (ids = [])
    );
  }

  return ids;
}
Example #4
Source File: macro-assertions.ts    From typecheck.macro with MIT License 6 votes vote down vote up
export function getTypeParameter(
  macroPath: NodePath<t.Node>
): NodePath<t.TSType> {
  const callExpr = macroPath.parentPath;
  assertCallExpr(callExpr);
  const typeParametersPath = callExpr.get("typeParameters");
  if (
    typeParametersPath &&
    !Array.isArray(typeParametersPath) &&
    typeParametersPath.node
  ) {
    const { node } = typeParametersPath;
    if (t.isTSTypeParameterInstantiation(node)) {
      const params = node.params.length;
      if (params != 1)
        throw new MacroError(Errors.MoreThanOneTypeParameter(params));
      const typeParameterPath = typeParametersPath.get("params.0");
      if (
        !Array.isArray(typeParameterPath) &&
        // @ts-ignore: https://github.com/babel/babel/issues/11535
        typeParameterPath.isTSType()
      ) {
        // @ts-ignore: https://github.com/babel/babel/issues/11535
        return typeParameterPath;
      }
      throwUnexpectedError(
        `typeParameter was ${
          Array.isArray(typeParameterPath) ? "an array" : "not a TSType"
        }`
      );
    }
    throwUnexpectedError(
      `typeParameters node was ${node.type} instead of TSTypeParameterInstantiation`
    );
  } else {
    throw new MacroError(Errors.NoTypeParameters());
  }
}
Example #5
Source File: scanDeepDependencies.ts    From vidact with MIT License 6 votes vote down vote up
function extractNames(path: NodePath<t.VariableDeclaration>): string[] {
  const { id } = path.node.declarations[0];

  if (t.isIdentifier(id)) {
    return [id.name];
  } else if (t.isArrayPattern(id)) {
    return (id.elements as t.Identifier[]).map(({ name }) => name);
  }

  return [];
}
Example #6
Source File: macro-assertions.ts    From typecheck.macro with MIT License 6 votes vote down vote up
export function getBlockParent(macroPath: NodePath<t.Node>): t.Statement[] {
  const callExpr = macroPath.parentPath;
  assertCallExpr(callExpr);
  const exprStmt = callExpr.parentPath;
  if (!exprStmt.isExpressionStatement())
    throw new MacroError(Errors.InvalidRegisterCall());

  const { node } = exprStmt.parentPath;
  if (!t.isProgram(node) && !t.isBlock(node)) {
    throw new MacroError(Errors.InvalidRegisterCall());
  } else {
    return node.body;
  }
}
Example #7
Source File: plugin.ts    From vite-react-jsx with MIT License 6 votes vote down vote up
/**
 * Replace this:
 *
 *     import { jsx as _jsx } from "react/jsx-runtime"
 *
 * with this:
 *
 *     var _jsx = require("react/jsx-runtime").jsx
 */
export function babelImportToRequire({
  types: t,
}: typeof import('@babel/core')) {
  return {
    visitor: {
      ImportDeclaration(path: NodePath) {
        const decl = path.node as ImportDeclaration
        const spec = decl.specifiers[0] as ImportSpecifier

        path.replaceWith(
          t.variableDeclaration('var', [
            t.variableDeclarator(
              spec.local,
              t.memberExpression(
                t.callExpression(t.identifier('require'), [decl.source]),
                spec.imported
              )
            ),
          ])
        )
      },
    },
  }
}
Example #8
Source File: dump-helpers.ts    From typecheck.macro with MIT License 6 votes vote down vote up
export function replaceWithCode(
  code: string,
  path: NodePath<t.Node>,
  filename: string
): void {
  const ast = parse(code, { filename });
  if (t.isFile(ast)) {
    path.replaceWith(ast.program.body[0]);
  } else {
    throwUnexpectedError(
      `${code} was incorrectly parsed. The AST was: ${JSON.stringify(ast)}`
    );
  }
}
Example #9
Source File: createStateDefinition.ts    From vidact with MIT License 6 votes vote down vote up
export function createStateDefinition(
  state: ComponentState,
  fnPath: NodePath<t.FunctionDeclaration>
) {
  const states = state.state;

  const internalStateDeclaration = t.variableDeclaration("const", [
    t.variableDeclarator(
      t.identifier(STATE_VAR),
      t.objectExpression(
        states.map(({ name, initialValue }) =>
          t.objectProperty(name, initialValue || t.identifier("undefined"))
        )
      )
    )
  ]);

  const defineUpdater = t.variableDeclaration("const", [
    t.variableDeclarator(
      t.identifier(KEY_STATE_UPDATER),
      createUpdatableUpdater(fnPath.get("body"), state, "state")
    )
  ]);

  return [internalStateDeclaration, defineUpdater];
}
Example #10
Source File: img.ts    From react-optimized-image with MIT License 6 votes vote down vote up
transformImgComponent = (types: Babel['types'], path: NodePath<JSXElement>): void => {
  // abort if it has already the rawSrc attribute
  if (getAttribute(path, 'rawSrc')) {
    return;
  }

  // get src attribute
  const src = getAttribute(path, 'src');
  const requireArgs = src ? getRequireArguments(types, src) : undefined;

  if (!src || !requireArgs) {
    return;
  }

  const config = buildConfig(types, path);

  const query: Record<string, string> = {};

  // add boolean queries
  ['inline', 'url', 'original'].forEach((attr) => {
    if ((config as Record<string, unknown>)[attr] === true) {
      query[attr] = '';
    }
  });

  // transfer original src attribute if a new query param needs to be set
  if (Object.keys(query).length > 0) {
    (src.get('value') as NodePath).replaceWith(
      types.jsxExpressionContainer(buildRequireStatement(types, clone(requireArgs), query)),
    );
  }

  const rawSrc = buildRawSrcAttribute(types, requireArgs, config, query);
  (path.get('openingElement') as NodePath<JSXOpeningElement>).pushContainer('attributes', rawSrc);
}
Example #11
Source File: getStatementUpdaterIdentifier.ts    From vidact with MIT License 6 votes vote down vote up
export default function getStatementUpdaterIdentifier(
  path: NodePath<t.VariableDeclaration>
): string {
  const { node: id } = path.get("declarations")[0].get("id");
  if (t.isIdentifier(id)) {
    return id.name;
  }
  if (t.isArrayPattern(id) && t.isIdentifier(id.elements[0])) {
    return id.elements[0].name;
  }
  throw new Error(
    "Statement updater declarator must be an Identifier or ArrayPattern"
  );
}
Example #12
Source File: traverse.ts    From react-optimized-image with MIT License 6 votes vote down vote up
getRequireArguments = (
  types: Babel['types'],
  path: NodePath<JSXAttribute>,
): CallExpression['arguments'] | undefined => {
  // check for inline-require statement
  if (
    path.node.value &&
    path.node.value.type === 'JSXExpressionContainer' &&
    path.node.value.expression.type === 'CallExpression' &&
    path.node.value.expression.callee.type === 'Identifier' &&
    path.node.value.expression.callee.name === 'require' &&
    path.node.value.expression.arguments.length > 0
  ) {
    return path.node.value.expression.arguments;
  }

  // check for import reference
  if (
    path.node.value &&
    path.node.value.type === 'JSXExpressionContainer' &&
    path.node.value.expression.type === 'Identifier'
  ) {
    const variableName = path.node.value.expression.name;
    const binding = path.scope.getBinding(variableName);

    if (
      binding &&
      binding.kind === 'module' &&
      isImport(binding.path) &&
      binding.path.parent.type === 'ImportDeclaration'
    ) {
      return [types.stringLiteral(binding.path.parent.source.value)];
    }
  }
}
Example #13
Source File: plugin.ts    From telefunc with MIT License 6 votes vote down vote up
function isFileAlreadyTransformed(path: NodePath<BabelTypes.Program>, types: typeof BabelTypes): boolean {
  return path.node.body.some((t) => {
    if (!types.isImportDeclaration(t)) return false
    if (t.specifiers.length === 0) return false

    const specifier = t.specifiers[0]
    if (!types.isImportSpecifier(specifier)) return false
    if (!types.isImportSpecifier(specifier)) return false
    if (!types.isIdentifier(specifier.imported)) return false

    return specifier.imported.name === '__internal_fetchTelefunc'
  })
}
Example #14
Source File: svg.ts    From react-optimized-image with MIT License 6 votes vote down vote up
transformSvgComponent = (types: Babel['types'], path: NodePath<JSXElement>): void => {
  // abort if it has already the rawSrc attribute
  if (getAttribute(path, 'rawSrc')) {
    return;
  }

  const src = getAttribute(path, 'src');
  const requireArgs = src ? getRequireArguments(types, src) : undefined;

  if (!src || !requireArgs) {
    return;
  }

  const rawSrc = buildRawSrcAttribute(types, requireArgs);
  (path.get('openingElement') as NodePath<JSXOpeningElement>).pushContainer('attributes', rawSrc);
  src.remove();
}
Example #15
Source File: index.ts    From glaze with MIT License 6 votes vote down vote up
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function findNearestParentComponent(path: NodePath<any>): NodePath | undefined {
  while (path) {
    const { scope } = path;
    const node = path.scope.path;

    if (types.isExportDefaultDeclaration(scope.path.parent)) {
      return node;
    }

    const functionIdentifier = getFunctionIdentifier(scope.path);

    if (functionIdentifier) {
      if (isComponent(functionIdentifier) || isHook(functionIdentifier)) {
        return node;
      }
    }
    path = scope.path.parentPath;
  }
  return undefined;
}
Example #16
Source File: jsx.ts    From react-optimized-image with MIT License 6 votes vote down vote up
getBooleanAttribute = (path: NodePath<JSXElement>, attributeName: string): boolean | undefined => {
  const attribute = getAttribute(path, attributeName);

  if (attribute) {
    if (attribute.node.value === null) {
      return true;
    }

    if (
      attribute.node.value.type === 'JSXExpressionContainer' &&
      attribute.node.value.expression.type === 'BooleanLiteral'
    ) {
      return attribute.node.value.expression.value;
    }

    // todo: better error message with link to docs when ready & create test for this error
    throw attribute.get('value').buildCodeFrameError('Only static boolean values are allowed');
  }

  return undefined;
}
Example #17
Source File: index.ts    From vanilla-extract with MIT License 5 votes vote down vote up
getDebugId = (path: NodePath<t.CallExpression>) => {
  const firstRelevantParentPath = path.findParent(
    ({ node }) => !(t.isCallExpression(node) || t.isSequenceExpression(node)),
  );

  if (!firstRelevantParentPath) {
    return;
  }

  // Special case: Handle `export const [themeClass, vars] = createTheme({});`
  // when it's already been compiled into this:
  //
  // var _createTheme = createTheme({}),
  //   _createTheme2 = _slicedToArray(_createTheme, 2),
  //   themeClass = _createTheme2[0],
  //   vars = _createTheme2[1];
  if (
    t.isVariableDeclaration(firstRelevantParentPath.parent) &&
    firstRelevantParentPath.parent.declarations.length === 4
  ) {
    const [themeDeclarator, , classNameDeclarator] =
      firstRelevantParentPath.parent.declarations;

    if (
      t.isCallExpression(themeDeclarator.init) &&
      t.isIdentifier(themeDeclarator.init.callee, { name: 'createTheme' }) &&
      t.isVariableDeclarator(classNameDeclarator) &&
      t.isIdentifier(classNameDeclarator.id)
    ) {
      return classNameDeclarator.id.name;
    }
  }

  const relevantParent = firstRelevantParentPath.node;

  if (
    t.isObjectProperty(relevantParent) ||
    t.isReturnStatement(relevantParent) ||
    t.isArrowFunctionExpression(relevantParent) ||
    t.isArrayExpression(relevantParent) ||
    t.isSpreadElement(relevantParent)
  ) {
    const names: Array<string> = [];

    path.findParent(({ node }) => {
      const name = extractName(node);
      if (name) {
        names.unshift(name);
      }
      // Traverse all the way to the root
      return false;
    });

    return names.join('_');
  } else {
    return extractName(relevantParent);
  }
}
Example #18
Source File: createUpdatableUpdater.ts    From vidact with MIT License 5 votes vote down vote up
findCallee = (name: string) => (o: NodePath) => {
  return isCallExpressionWithName(o.node, name);
}
Example #19
Source File: traverse.ts    From react-optimized-image with MIT License 5 votes vote down vote up
isImport = (path: NodePath): boolean => {
  return path.type === 'ImportSpecifier' || path.type === 'ImportDefaultSpecifier';
}
Example #20
Source File: macro-assertions.ts    From typecheck.macro with MIT License 5 votes vote down vote up
function assertCallExpr(
  expr: NodePath<t.Node>
): asserts expr is NodePath<t.Node> & NodePath<t.CallExpression> {
  if (!expr.isCallExpression())
    throw new MacroError(Errors.NotCalledAsFunction(expr.type));
}
Example #21
Source File: img.ts    From react-optimized-image with MIT License 5 votes vote down vote up
buildConfig = (types: Babel['types'], path: NodePath<JSXElement>): ImageConfig => {
  // build config
  let config: ImageConfig = { ...(globalImageConfig.default || {}) };

  // check if a specific type is set
  const type = getTypeAttribute(path, Object.keys(globalImageConfig.types || {}));

  // add type configs
  if (type && globalImageConfig.types && globalImageConfig.types[type]) {
    config = { ...config, ...globalImageConfig.types[type] };
  }

  // check boolean attributes: webp, inline, url, original
  ['webp', 'inline', 'url', 'original'].forEach((attr) => {
    const value = getBooleanAttribute(path, attr);

    if (typeof value !== 'undefined') {
      (config as Record<string, unknown>)[attr] = value;
    } else if (typeof value === 'undefined' && (config as Record<string, unknown>)[attr] === true) {
      // add attr from global image config
      (path.get('openingElement') as NodePath<JSXOpeningElement>).pushContainer(
        'attributes',
        types.jsxAttribute(types.jsxIdentifier(attr), null),
      );
    }
  });

  // get sizes
  const sizes = getNumberedArrayAttribute(path, 'sizes');

  if (typeof sizes !== 'undefined') {
    config.sizes = sizes;
  } else if (config.sizes) {
    // add sizes attr from global image config
    (path.get('openingElement') as NodePath<JSXOpeningElement>).pushContainer(
      'attributes',
      types.jsxAttribute(
        types.jsxIdentifier('sizes'),
        types.jsxExpressionContainer(types.arrayExpression(config.sizes.map((size) => types.numericLiteral(size)))),
      ),
    );
  }

  // get densities
  const densities = getNumberedArrayAttribute(path, 'densities');

  if (typeof densities !== 'undefined') {
    config.densities = densities;
  } else if (config.densities) {
    // add densities attr from global image config
    (path.get('openingElement') as NodePath<JSXOpeningElement>).pushContainer(
      'attributes',
      types.jsxAttribute(
        types.jsxIdentifier('densities'),
        types.jsxExpressionContainer(types.arrayExpression(config.densities.map((size) => types.numericLiteral(size)))),
      ),
    );
  }

  // get breakpoints
  const breakpoints = getNumberedArrayAttribute(path, 'breakpoints');

  if (typeof breakpoints !== 'undefined') {
    config.breakpoints = breakpoints;
  } else if (config.breakpoints) {
    // add breakpoints attr from global image config
    (path.get('openingElement') as NodePath<JSXOpeningElement>).pushContainer(
      'attributes',
      types.jsxAttribute(
        types.jsxIdentifier('breakpoints'),
        types.jsxExpressionContainer(
          types.arrayExpression(config.breakpoints.map((size) => types.numericLiteral(size))),
        ),
      ),
    );
  }

  return config;
}
Example #22
Source File: annotations.ts    From vidact with MIT License 5 votes vote down vote up
export function annotate(path: NodePath, annotation: Annotation) {
  path.addComment("leading", " @vidact-" + annotation, true);
}
Example #23
Source File: index.ts    From design-systems-cli with MIT License 5 votes vote down vote up
/**
 * Find imports of @design-system/cli components and automatically includes their styles.
 */
export default function includeStyles(
  { types }: Babel,
  { scope }: IncludeStylesOptions
): babel.PluginObj {
  const SCOPE_REGEX = new RegExp(`^@${scope}\\/`);
  let cssImports = new Set<string>();

  /** Insert an import for a component */
  function insertCssImports(
    path: NodePath<BabelTypes.ImportDeclaration>,
    importName: string
  ) {
    if (!SCOPE_REGEX.test(importName)) {
      return;
    }

    if (importName.match(/\.css$/)) {
      // Only keep 1 import per imported css file
      if (cssImports.has(importName)) {
        path.remove();
      } else {
        cssImports.add(importName);
      }

      return;
    }

    const cssImport = `${importName}/dist/main.css`;

    if (exists(cssImport)) {
      path.insertAfter(
        types.importDeclaration([], types.stringLiteral(cssImport))
      );
    }

    // After walk dependencies to find more css to include
    const packageJson = resolvePackage(importName)

    if (!packageJson) {
      return;
    }

    // After walk dependencies to find more css to include
    // eslint-disable-next-line global-require, import/no-dynamic-require, @typescript-eslint/no-var-requires
    const { dependencies = {}, name } = require(packageJson);

    // In case we happen to find a package.json that is for the specified scope
    if (!SCOPE_REGEX.test(name)) {
      return;
    }

    Object.keys(dependencies).forEach((dependency: string) =>
      insertCssImports(path, dependency)
    );
  }

  return {
    /** Initialization code */
    pre: () => {
      cssImports = new Set<string>();
    },

    visitor: {
      ImportDeclaration(path) {
        const importName = path.node.source.value;
        insertCssImports(path, importName);
      }
    }
  };
}
Example #24
Source File: scanDeepDependencies.ts    From vidact with MIT License 5 votes vote down vote up
function isUpdater(path: NodePath<t.VariableDeclaration>) {
  const { id } = path.node.declarations[0];

  return t.isIdentifier(id) &&
    (isStatementExecuter(id.name) || isElementUpdate(id.name))
    ? id
    : undefined;
}
Example #25
Source File: index.ts    From design-systems-cli with MIT License 5 votes vote down vote up
/**
 * Find imports of @design-system/cli components and optionally replaces their style imports.
 */
export default function replaceStyles(
  { types }: Babel,
  { scope, use, replace = 'main' }: ReplaceStylesOptions
): babel.PluginObj {
  /**  Replace an import inside a component */
  function replaceCssImports(
    path: NodePath<BabelTypes.ImportDeclaration>,
    state: PluginPass,
    importName: string
  ) {
    const isScope =
      state?.file?.opts?.filename &&
      state.file.opts.filename.includes(`@${scope}/`);

    if (isScope && importName.includes(`${replace}.css`)) {
      // We found a candidate for replacement
      const dirname = nodePath.dirname(state.file.opts.filename as string);
      const next = nodePath.join(dirname, importName.replace(replace, use));

      if (exists(next)) {
        // Replacement exists
        const importDeclaration = types.importDeclaration(
          [],
          types.stringLiteral(`../${use}.css`)
        );
        path.replaceInline(importDeclaration);
      }
    }
  }

  /**  Replace an css.js import inside a component */
  function replaceCssJsImports(
    path: NodePath<BabelTypes.ImportDeclaration>,
    state: PluginPass,
    importName: string
  ) {
    const isScope =
      state?.file?.opts?.filename &&
      state.file.opts.filename.includes(`@${scope}/`);

    if (
      isScope &&
      path.node.specifiers.length &&
      importName.includes('.css') &&
      !importName.includes(use)
    ) {
      const dirname = nodePath.dirname(state.file.opts.filename as string);
      const newImport = importName.replace('.css', `-${use}.css`);
      const newImportPath = nodePath.join(dirname, newImport);

      if (exists(newImportPath)) {
        const importDeclaration = types.importDeclaration(
          path.node.specifiers,
          types.stringLiteral(newImport)
        );
        path.replaceWith(importDeclaration);
      }
    }
  }

  return {
    visitor: {
      ImportDeclaration(path, state) {
        const importName = path.node.source.value;
        replaceCssImports(path, state, importName);
        replaceCssJsImports(path, state, importName);
      },
    },
  };
}
Example #26
Source File: index.ts    From glaze with MIT License 5 votes vote down vote up
/* Source: https://github.com/facebook/react/blob/master/packages/eslint-plugin-react-hooks/src/RulesOfHooks.js#L542 */
function getFunctionIdentifier(path: NodePath): types.Identifier | undefined {
  const { node } = path;
  const { parent } = path;

  if (path.isFunctionDeclaration()) {
    return path.node.id || undefined;
  }
  if (path.isFunctionExpression()) {
    return path.node.id || undefined;
  }

  if (path.isFunctionExpression() || path.isArrowFunctionExpression()) {
    if (
      types.isVariableDeclarator(parent) &&
      parent.init === node &&
      types.isIdentifier(parent.id)
    ) {
      return parent.id;
    }
    if (
      types.isAssignmentExpression(parent) &&
      parent.right === node &&
      parent.operator === '=' &&
      types.isIdentifier(parent.left)
    ) {
      return parent.left;
    }
    if (
      types.isProperty(parent) &&
      parent.value === node &&
      types.isIdentifier(parent.key)
    ) {
      return parent.key;
    }
    if (
      types.isAssignmentPattern(parent) &&
      parent.right === node &&
      types.isIdentifier(parent.left)
    ) {
      return parent.left;
    }
  } else {
    return undefined;
  }
  return undefined;
}
Example #27
Source File: normalizeUseMemo.ts    From vidact with MIT License 5 votes vote down vote up
export function normalizeUseMemo(
  path: NodePath<t.CallExpression>,
  state: ComponentState
) {
  if (!t.isIdentifier(path.node.callee)) {
    return;
  }
  const calleeName = path.node.callee.name;

  const isMemo = calleeName === USE_MEMO;
  const isCallback = calleeName === USE_CALLBACK;

  if (!isCallback && !isMemo) {
    return;
  }

  const [memoizedValue, dependencies] = path.get("arguments");

  if (!memoizedValue || !dependencies) {
    throw path.buildCodeFrameError(`${calleeName} expects 2 arugments.`);
  }

  if (
    !memoizedValue.isFunctionExpression() &&
    !memoizedValue.isArrowFunctionExpression()
  ) {
    throw path.buildCodeFrameError(
      `The first argument of ${calleeName} must be a function expression.`
    );
  }

  if (!dependencies.isArrayExpression()) {
    throw path.buildCodeFrameError(
      `The second argument of ${calleeName} must be an array of dependencies.`
    );
  }

  const statement = path.getStatementParent();
  annotate(statement, calleeName);
  annotate(statement, "locked");

  if (isMemo) {
    path.replaceWith(t.callExpression(memoizedValue.node, []));
  } else {
    path.replaceWith(memoizedValue.node);
  }

  if (dependencies.get("elements").length === 0) {
    return;
  }

  if (!statement.isVariableDeclaration()) {
    throw statement.buildCodeFrameError(
      `${calleeName} must be used with variable declaration`
    );
  }

  const names = declarationToAssignment(statement);
  names.forEach((name) => {
    state.variablesWithDependencies.add(name);
    state.variableStatementDependencyManager.push(
      { type: "local", name },
      { type: "node", value: statement }
    );
  });

  const descriptors = addHookDependencyCheck(dependencies, memoizedValue);

  descriptors.forEach((descriptor) => {
    names.forEach((name) => {
      state.variableStatementDependencyManager.push(descriptor, {
        type: "local",
        name,
      });
    });
  });
}
Example #28
Source File: plugin.ts    From telefunc with MIT License 5 votes vote down vote up
function getExportsFromBabelAST(programNodePath: NodePath<BabelTypes.Program>, types: typeof BabelTypes) {
  const body = programNodePath.node.body

  const exported = []
  for (let index = 0; index < body.length; index++) {
    const subNode = body[index]

    // export default fnName
    if (types.isExportDefaultDeclaration(subNode)) {
      exported.push('default')
    }

    if (types.isExportNamedDeclaration(subNode)) {
      if (subNode.specifiers.length > 0) {
        // Handles cases:
        // export { functionName };
        // export { functionName as fnName };
        // export { functionName as "fnName" };
        // export { "fnName" } from "package";
        for (const specifier of subNode.specifiers) {
          if (specifier.exported.type === 'Identifier') {
            // export { functionName };
            // export { functionName as fnName };
            exported.push(specifier.exported.name)
          } else if (specifier.exported.type === 'StringLiteral') {
            // export { functionName as "fnName" };
            // export { "fnName" } from "package";
            exported.push(specifier.exported.value)
          }
        }
      } else if (types.isFunctionDeclaration(subNode.declaration)) {
        // export function fn() {}
        // export async function fn() {}
        exported.push(subNode.declaration.id!.name) // Function must have ID if it's part of a named export
      } else if (types.isVariableDeclaration(subNode.declaration)) {
        // export const fnName = () => {}
        // export var fnName = () => {}
        // export let fnName = () => {}
        // export const fnName = function() {}
        // export var fnName = function() {}
        // export let fnName = function() {}
        const declarator = subNode.declaration.declarations[0]!
        if (
          'name' in declarator.id &&
          (types.isFunctionExpression(declarator.init) || types.isArrowFunctionExpression(declarator.init))
        ) {
          exported.push(declarator.id.name) // Function must have ID if it's part of a named export
        }
      }
    }
  }

  return exported
}
Example #29
Source File: add-exports-array.ts    From mpflow with MIT License 4 votes vote down vote up
/**
 * 向配置文件如
 * module.exports = { plugins: [] }
 * 中的 plugins 添加插件信息
 */
export default function (api: typeof babel, options: Options): PluginObj {
  const { types: t, template } = api
  const { fieldName, items } = options

  let pluginsArrayExpression: NodePath<ArrayExpression> | null

  if (!fieldName || !items || !items.length)
    return {
      name: 'add-exports-array',
      visitor: {},
    }

  return {
    name: 'add-exports-array',
    pre() {
      pluginsArrayExpression = null
    },
    visitor: {
      AssignmentExpression(p) {
        // 寻找 module.exports = { plugins: [] }
        if (
          !m
            .assignmentExpression(
              '=',
              m.memberExpression(m.identifier('module'), m.identifier('exports')),
              m.objectExpression(),
            )
            .match(p.node)
        )
          return

        const objectExpression = p.get('right') as NodePath<ObjectExpression>
        const properties = objectExpression.get('properties')

        properties.forEach(property => {
          if (
            !m
              .objectProperty(m.or(m.stringLiteral(fieldName), m.identifier(fieldName)), m.arrayExpression())
              .match(property.node)
          )
            return

          pluginsArrayExpression = property.get('value') as NodePath<ArrayExpression>
        })
      },
      Program: {
        exit(p) {
          if (!pluginsArrayExpression) {
            // 如果找不到 module.exports = { plugins: [] }
            // 则在末尾加一句 exports.plugins = (exports.plugins || []).concat([])
            const statement = template.statement(`
              exports.FIELD_NAME = (exports.FIELD_NAME || []).concat([]);
            `)({
              FIELD_NAME: t.identifier(fieldName),
            })
            const [statementPath] = p.pushContainer('body', statement)
            pluginsArrayExpression = statementPath.get('expression.right.arguments.0') as NodePath<ArrayExpression>
          }
          const targetArray = pluginsArrayExpression
          // 添加 item
          items.forEach(item => {
            const [pluginName, option] = Array.isArray(item) ? item : [item]
            if (!option) {
              targetArray.pushContainer('elements', t.stringLiteral(pluginName))
            } else {
              targetArray.pushContainer(
                'elements',
                t.arrayExpression([t.stringLiteral(pluginName), template.expression(JSON.stringify(option))()]),
              )
            }
          })
        },
      },
    },
  }
}