@babel/core#PluginObj TypeScript Examples
The following examples show how to use
@babel/core#PluginObj.
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: babel.ts From xwind with MIT License | 6 votes |
function babel(
babel: typeof Babel,
config: { config?: string },
workingPath: string
): PluginObj<PluginPass> {
const { types: t } = babel;
const twConfigPath = getTwConfigPath(config.config);
return {
name: "xwind",
visitor: {
ImportDefaultSpecifier(path, state) {
//get referencePaths for default import from "xwind" or "xwind/macro"
if (!path.parentPath.isImportDeclaration()) return;
if (path.parent.type !== "ImportDeclaration") return;
if (!MODULES.includes(path.parent.source.value)) return;
const referencePaths =
path.scope.bindings[path.node.local.name].referencePaths;
//remove default import and remove import if no other specifiers exist
path.remove();
if (path.parent.specifiers.length === 0) {
path.parentPath.remove();
}
if (!referencePaths.length) return;
const transformer = getCachedTransformer(twConfigPath);
transformer(referencePaths, state, t);
},
},
};
}
Example #2
Source File: plugin.ts From reskript with MIT License | 6 votes |
plugin = (): PluginObj => {
return {
visitor: {
Program(path) {
const callee = prepareReactImport(path, 'useEffect');
path.pushContainer('body', callee);
},
},
};
}
Example #3
Source File: plugin.ts From telefunc with MIT License | 6 votes |
export default function BabelPluginTelefunc(babel: { types: typeof BabelTypes }): PluginObj {
return {
visitor: {
Program(path, context) {
const filename: string = context.filename!
if (!filename.includes('.telefunc.')) return
if (isFileAlreadyTransformed(path, babel.types)) return
const exportList = getExportsFromBabelAST(path, babel.types)
const root: string = context.file.opts.root!
const transformed = transformTelefuncFileSync(toPosixPath(filename), toPosixPath(root), exportList).code
const parsed = parse(transformed, {
sourceType: 'module'
})
path.replaceWith(parsed.program)
}
}
}
}
Example #4
Source File: index.ts From react-dev-inspector with MIT License | 6 votes |
export function InspectorBabelPlugin(babel, options?: InspectorPluginOptions): PluginObj {
return {
name: 'react-dev-inspector-babel-plugin',
visitor: createVisitor({
cwd: options?.cwd,
excludes: [
'node_modules/',
...options?.excludes ?? [],
],
}),
}
}
Example #5
Source File: translate.ts From nota with MIT License | 5 votes |
optimizePlugin = (): PluginObj => ({
visitor: {
ArrayExpression(path) {
// [...[e1, e2]] => [e1, e2]
path.node.elements = path.node.elements
.map(el => {
if (el && el.type == "SpreadElement" && el.argument.type == "ArrayExpression") {
return el.argument.elements;
} else {
return [el];
}
})
.flat();
},
ObjectExpression(path) {
let props = path.node.properties;
/// {...e} => e
if (props.length == 1 && props[0].type == "SpreadElement") {
path.replaceWith(props[0].argument);
}
},
CallExpression(path) {
let expr = path.node;
if (
expr.arguments.length == 0 &&
expr.arguments.length == 0 &&
expr.callee.type == "ArrowFunctionExpression" &&
expr.callee.body.type == "BlockStatement" &&
expr.callee.body.body.length == 1 &&
expr.callee.body.body[0].type == "ReturnStatement" &&
expr.callee.body.body[0].argument
) {
// `(() => { return e; })()` => `e`
path.replaceWith(expr.callee.body.body[0].argument);
path.visit();
} else {
path.node.arguments = path.node.arguments
.map(arg => {
// f(...[x, y]) => f(x, y)
if (arg.type == "SpreadElement" && arg.argument.type == "ArrayExpression") {
return arg.argument.elements.map(el => el!);
} else {
return [arg];
}
})
.flat();
}
},
},
})
Example #6
Source File: Generator.test.ts From mpflow with MIT License | 5 votes |
describe('Generator', () => {
test('should extend package', async () => {
const volume = new Volume()
volume.fromJSON({
'/package.json': '{"name": "test"}',
})
const fs = createFsFromVolume(volume)
const generator = new Generator('/', { inputFileSystem: fs as any, outputFileSystem: fs as any })
generator.extendPackage(
{
scripts: {
test: 'echo "test"',
},
dependencies: {
module: '^1.0.0',
},
},
'test',
)
await generator.generate()
expect(JSON.parse(fs.readFileSync('/package.json', 'utf8') as string)).toEqual({
name: 'test',
scripts: {
test: 'echo "test"',
},
dependencies: {
module: '^1.0.0',
},
})
})
test('should process file', async () => {
const volume = new Volume()
volume.fromJSON({
'/modify.js': 'before modify',
'/rename.js': 'before rename',
'/transform.js': 'module.exports = "before transform";',
})
const fs = createFsFromVolume(volume)
const generator = new Generator('/', { inputFileSystem: fs as any, outputFileSystem: fs as any })
generator.processFile('test modify', 'modify.js', (fileInfo, api) => {
api.replace('after modify')
})
generator.processFile('test rename', 'rename.js', (fileInfo, api) => {
api.rename('rename.ts')
})
generator.processFile('test transform', 'transform.js', (fileInfo, api) => {
api.transform(
({ types }: typeof babel): PluginObj => ({
name: 'test transform',
visitor: {
StringLiteral(p) {
p.node.value = 'after transform'
},
},
}),
{},
)
})
await generator.generate(false)
expect(volume.toJSON()).toEqual({
'/modify.js': 'after modify',
'/rename.ts': 'before rename',
'/transform.js': 'module.exports = "after transform";',
})
})
})
Example #7
Source File: index.ts From react-loosely-lazy with Apache License 2.0 | 4 votes |
export default function ({
types: t,
}: {
types: typeof BabelTypes;
}): PluginObj {
function getCallExpression(path: NodePath) {
let maybeCallExpression = path.parentPath;
if (
maybeCallExpression.isMemberExpression() &&
!maybeCallExpression.node.computed &&
t.isIdentifier(maybeCallExpression.get('property'), { name: 'Map' })
) {
maybeCallExpression = maybeCallExpression.parentPath;
}
return maybeCallExpression;
}
function getLazyArguments(
callExpression: NodePath<BabelTypes.CallExpression>
): [NodePath<BabelTypes.Function>, NodePath<BabelTypes.ObjectExpression>] {
const args = callExpression.get<'arguments'>('arguments');
const loader = args[0];
let options = args[1];
if (!loader.isFunction()) {
throw new Error('Loader argument must be a function');
}
if (!options || !options.isObjectExpression()) {
callExpression.node.arguments.push(t.objectExpression([]));
options = callExpression.get<'arguments'>('arguments')[1];
return [loader, options as NodePath<BabelTypes.ObjectExpression>];
}
return [loader, options];
}
type PropertiesMap = Map<
string,
NodePath<BabelTypes.ObjectMethod | BabelTypes.ObjectProperty>
>;
function getPropertiesMap(
options: NodePath<BabelTypes.ObjectExpression>
): PropertiesMap {
const properties = options.get<'properties'>('properties');
return properties.reduce<PropertiesMap>((map, property) => {
if (property.isSpreadElement()) {
throw new Error(
'Options argument does not support SpreadElement as it is not statically analyzable'
);
}
// @ts-expect-error TypeScript type narrowing does not work correctly here
map.set(property.node.key.name, property);
return map;
}, new Map());
}
function getSSR(map: PropertiesMap, lazyMethodName: string): boolean {
const property = map.get('ssr');
if (!property) {
// @ts-ignore This should be available
return DEFAULT_OPTIONS[lazyMethodName].ssr;
}
if (property.isObjectMethod()) {
throw new Error('Unable to statically analyze ssr option');
}
// @ts-expect-error TypeScript type narrowing does not work correctly here
const value = property.node.value;
if (!t.isBooleanLiteral(value)) {
throw new Error('Unable to statically analyze ssr option');
}
return value.value;
}
type TransformLoaderOptions = {
env: 'client' | 'server';
loader: NodePath<BabelTypes.Function>;
noopRedundantLoaders: boolean;
ssr: boolean;
};
// TODO Remove the require hack when this library drops non-streaming support
function transformLoader({
env,
loader,
noopRedundantLoaders,
ssr,
}: TransformLoaderOptions): { importPath: string | void } {
let importPath;
loader.traverse({
Import(nodePath: NodePath<BabelTypes.Import>) {
const maybeImportCallExpression = nodePath.parentPath;
if (!maybeImportCallExpression.isCallExpression()) {
return;
}
// Get the import path when the parent is a CallExpression and its first
// argument is a StringLiteral
const maybeImportPath =
maybeImportCallExpression.get<'arguments'>('arguments')[0];
if (maybeImportPath.isStringLiteral()) {
importPath = maybeImportPath.node.value;
}
// Do not transform the loader on the client
if (env === 'client') {
return;
}
// When on the server, either do nothing or replace the loader with a noop
if (!ssr) {
if (noopRedundantLoaders) {
const noopComponent = t.arrowFunctionExpression(
[],
t.nullLiteral()
);
loader.replaceWith(t.arrowFunctionExpression([], noopComponent));
}
return;
}
// Replace the import with a require
nodePath.replaceWith(t.identifier('require'));
// Transform all then calls to be synchronous in order to support
// named exports
let maybeMemberExpression: NodePath<BabelTypes.Node> =
nodePath.parentPath.parentPath;
let previousIdOrExpression;
while (maybeMemberExpression.isMemberExpression()) {
const { property } = maybeMemberExpression.node;
if (!t.isIdentifier(property, { name: 'then' })) {
break;
}
const maybeCallExpression = maybeMemberExpression.parentPath;
if (!maybeCallExpression.isCallExpression()) {
break;
}
if (!previousIdOrExpression) {
const loaderId = loader.scope.generateUidIdentifier();
nodePath.scope.push({
id: loaderId,
init: maybeImportCallExpression.node,
});
previousIdOrExpression = loaderId;
}
const thenId = loader.scope.generateUidIdentifier();
const thenArgs = maybeCallExpression.get<'arguments'>('arguments');
const onFulfilled = thenArgs[0];
if (onFulfilled.isExpression()) {
nodePath.scope.push({
id: thenId,
init: onFulfilled.node,
});
}
const replacement = t.callExpression(thenId, [
previousIdOrExpression,
]);
maybeCallExpression.replaceWith(replacement);
maybeMemberExpression = maybeMemberExpression.parentPath.parentPath;
previousIdOrExpression = replacement;
}
},
AwaitExpression() {
throw new Error('Loader argument does not support await expressions');
},
MemberExpression(nodePath: NodePath<BabelTypes.MemberExpression>) {
const maybeCallExpression = nodePath.parentPath;
if (
t.isIdentifier(nodePath.node.property, { name: 'then' }) &&
maybeCallExpression.isCallExpression()
) {
const thenArgs = maybeCallExpression.get<'arguments'>('arguments');
if (thenArgs.length > 1) {
throw new Error(
'Loader argument does not support Promise.prototype.then with more than one argument'
);
}
}
if (t.isIdentifier(nodePath.node.property, { name: 'catch' })) {
throw new Error(
'Loader argument does not support Promise.prototype.catch'
);
}
},
});
return {
importPath,
};
}
function buildModuleIdProperty(opts: GetModulePathOptions) {
return t.objectProperty(
t.identifier(MODULE_ID_KEY),
t.stringLiteral(getModulePath(opts))
);
}
return {
visitor: {
ImportDeclaration(
path: NodePath<BabelTypes.ImportDeclaration>,
state: {
opts?: BabelPluginOptions | undefined | false;
filename?: string;
}
) {
const {
client,
modulePathReplacer,
noopRedundantLoaders = true,
resolverOptions = {},
} = state.opts || {};
const { filename } = state;
const source = path.node.source.value;
const customResolver = createCustomResolver(resolverOptions);
if (source !== PACKAGE_NAME) {
return;
}
const bindingNames = LAZY_METHODS;
const bindings = bindingNames
.map(name => path.scope.getBinding(name))
.filter(isPresent);
bindings.forEach(binding => {
const lazyMethodName = binding.identifier.name;
binding.referencePaths.forEach((refPath: NodePath) => {
const callExpression = getCallExpression(refPath);
if (!callExpression.isCallExpression()) {
return;
}
const [loader, lazyOptions] = getLazyArguments(callExpression);
const propertiesMap = getPropertiesMap(lazyOptions);
const { importPath } = transformLoader({
env: client ? 'client' : 'server',
loader,
noopRedundantLoaders,
ssr: getSSR(propertiesMap, lazyMethodName),
});
if (!importPath) {
return;
}
if (!filename) {
throw new Error(
`Babel transpilation target for ${importPath} not found`
);
}
// Add the moduleId property to options
lazyOptions.node.properties.push(
buildModuleIdProperty({
filename,
importPath,
modulePathReplacer,
resolver: customResolver,
})
);
});
});
},
},
};
}
Example #8
Source File: index.ts From glaze with MIT License | 4 votes |
module.exports = function plugin({
types: t,
}: {
types: typeof types;
}): PluginObj<{
hasSxProps: boolean | undefined;
useStylingImportName: string;
glazeImportDeclaration: types.ImportDeclaration | undefined;
pathsToAddHook: Set<NodePath<types.Function>>;
}> {
return {
name: 'babel-plugin-glaze',
visitor: {
Program: {
enter(path, state) {
state.hasSxProps = false;
state.useStylingImportName = 'useStyling';
state.pathsToAddHook = new Set();
const { node } = path;
// Check if something from glaze is already imported
const glazeImportDeclaration = node.body.find(
(s) => t.isImportDeclaration(s) && s.source.value === 'glaze',
);
// Something is already imported from glaze
if (t.isImportDeclaration(glazeImportDeclaration)) {
state.glazeImportDeclaration = glazeImportDeclaration;
// Check if it's `useStyling` which is imported
const useStylingImport = glazeImportDeclaration.specifiers.find(
(s) => t.isImportSpecifier(s) && s.imported.name === 'useStyling',
);
if (useStylingImport) {
state.useStylingImportName = useStylingImport.local.name;
}
}
},
exit(path, { hasSxProps, glazeImportDeclaration }) {
if (hasSxProps) {
const importUseStyling = template.ast`import { useStyling } from 'glaze'`;
// Something is already imported from glaze
if (glazeImportDeclaration) {
// Check if it's `useStyling` which is imported
const useStylingImport = glazeImportDeclaration.specifiers.find(
(s) =>
t.isImportSpecifier(s) && s.imported.name === 'useStyling',
);
// If it's not `useStyling`, we add it to the import
if (!useStylingImport) {
glazeImportDeclaration.specifiers.push(
t.importSpecifier(
t.identifier('useStyling'),
t.identifier('useStyling'),
),
);
}
}
// Nothing imported yet from glaze
else {
path.unshiftContainer('body', importUseStyling);
}
}
},
},
Function: {
exit(path, state) {
if (state.pathsToAddHook.has(path)) {
const nodeToAddHook = path.node;
const createUseStylingHook = template.statement.ast`
const sx = ${state.useStylingImportName}();
`;
if (t.isBlockStatement(nodeToAddHook.body)) {
/* Verify that the hook is not yet created.
If not,we create it
*/
const block = nodeToAddHook.body;
const isAlreadyImported = block.body.some(
(st) =>
t.isVariableDeclaration(st) &&
st.declarations.find(
(decl) =>
t.isCallExpression(decl.init) &&
t.isIdentifier(decl.init.callee, {
name: state.useStylingImportName,
}),
),
);
if (!isAlreadyImported) {
nodeToAddHook.body.body.unshift(createUseStylingHook);
}
} else {
/* Not a block statement. We first need to create one. Example:
const Comp = () => <div sx={{color: "blue"}}>hello</div>
Should become:
const Comp = () => {
return <div sx={{color: "blue"}}>hello</div>
}
*/
nodeToAddHook.body = t.blockStatement([
createUseStylingHook,
t.returnStatement(nodeToAddHook.body),
]);
}
}
},
},
JSXAttribute(path, state) {
const { node } = path;
if (t.isJSXIdentifier(node.name, { name: 'sx' })) {
const jsxIdentifier = node.name;
if (t.isJSXExpressionContainer(node.value)) {
if (t.isExpression(node.value.expression)) {
/* 1. We set this value so we know that somewhere in the file
the `sx` props is used.We will need therefore to import
`useStyling` hook from 'glaze'. This is done in the Program exit.
*/
state.hasSxProps = true;
/* 2. Find the nearest parent component (or the current scope)
and add it to a list that will be processed wihtin the
Function exit. This is where we will create de `sx` variable:
`const sx = useStyling()`
*/
const pathsToAddHook =
findNearestParentComponent(path) || path.scope.path;
if (pathsToAddHook.isFunction()) {
state.pathsToAddHook.add(pathsToAddHook);
}
/* 3. This is where we transform the `sx` props */
if (t.isJSXOpeningElement(path.parent)) {
const objectExpression = node.value.expression;
// Remove the `sx` props
path.remove();
// Check if a className props already exists
let classNameAttributeIdentifier = path.parent.attributes.find(
(a) =>
t.isJSXAttribute(a) &&
t.isJSXIdentifier(a.name, { name: 'className' }),
);
if (t.isJSXAttribute(classNameAttributeIdentifier)) {
// A className props already exists
const classNameNode = classNameAttributeIdentifier.value;
const baseTemplateLiteral = t.templateLiteral(
[
t.templateElement({ raw: '' }, false),
t.templateElement({ raw: '' }, true),
],
[
t.callExpression(t.identifier(jsxIdentifier.name), [
objectExpression,
]),
],
);
/* Handle the case where className is currently a
an expression. E.g: `className={fn(...)}` or
`className={isValid ? "my-class" : ""}`
*/
if (
t.isJSXExpressionContainer(classNameNode) &&
t.isExpression(classNameNode.expression)
) {
baseTemplateLiteral.quasis.splice(
1,
0,
t.templateElement({ raw: ' ' }, false),
);
baseTemplateLiteral.expressions.unshift(
classNameNode.expression,
);
} else if (t.isStringLiteral(classNameNode)) {
/* Handle the case where the className is currently a string */
if (classNameNode.value !== '') {
baseTemplateLiteral.quasis[0] = t.templateElement(
{ raw: `${classNameNode.value} ` },
false,
);
}
}
classNameAttributeIdentifier.value = t.jsxExpressionContainer(
baseTemplateLiteral,
);
} else {
/* Handle the case where no className exists yet */
classNameAttributeIdentifier = t.jsxAttribute(
t.jsxIdentifier('className'),
t.jsxExpressionContainer(
t.callExpression(t.identifier(jsxIdentifier.name), [
objectExpression,
]),
),
);
path.parent.attributes.unshift(classNameAttributeIdentifier);
}
}
}
}
}
},
},
};
};
Example #9
Source File: index.ts From vanilla-extract with MIT License | 4 votes |
export default function (): PluginObj<Context> {
return {
pre({ opts }) {
if (!opts.filename) {
// TODO Make error better
throw new Error('Filename must be available');
}
this.isESM = false;
this.isCssFile = cssFileFilter.test(opts.filename);
this.alreadyCompiled = false;
this.importIdentifiers = new Map();
this.namespaceImport = '';
const packageInfo = getPackageInfo(opts.cwd);
if (!packageInfo.name) {
throw new Error(
`Closest package.json (${packageInfo.path}) must specify name`,
);
}
this.packageName = packageInfo.name;
// Encode windows file paths as posix
this.filePath = posix.join(
...relative(packageInfo.dirname, opts.filename).split(sep),
);
},
visitor: {
Program: {
exit(path) {
if (this.isCssFile && !this.alreadyCompiled) {
// Wrap module with file scope calls
const buildSetFileScope = this.isESM
? buildSetFileScopeESM
: buildSetFileScopeCJS;
path.unshiftContainer(
'body',
buildSetFileScope({
filePath: t.stringLiteral(this.filePath),
packageName: t.stringLiteral(this.packageName),
}),
);
path.pushContainer('body', buildEndFileScope());
}
},
},
ImportDeclaration(path) {
this.isESM = true;
if (!this.isCssFile || this.alreadyCompiled) {
// Bail early if file isn't a .css.ts file or the file has already been compiled
return;
}
if (path.node.source.value === filescopePackageIdentifier) {
// If file scope import is found it means the file has already been compiled
this.alreadyCompiled = true;
return;
} else if (packageIdentifiers.has(path.node.source.value)) {
path.node.specifiers.forEach((specifier) => {
if (t.isImportNamespaceSpecifier(specifier)) {
this.namespaceImport = specifier.local.name;
} else if (t.isImportSpecifier(specifier)) {
const { imported, local } = specifier;
const importName = (
t.isIdentifier(imported) ? imported.name : imported.value
) as StyleFunction;
if (styleFunctions.includes(importName)) {
this.importIdentifiers.set(local.name, importName);
}
}
});
}
},
ExportDeclaration() {
this.isESM = true;
},
CallExpression(path) {
if (!this.isCssFile || this.alreadyCompiled) {
// Bail early if file isn't a .css.ts file or the file has already been compiled
return;
}
const { node } = path;
if (
t.isIdentifier(node.callee, { name: 'require' }) &&
t.isStringLiteral(node.arguments[0], {
value: filescopePackageIdentifier,
})
) {
// If file scope import is found it means the file has already been compiled
this.alreadyCompiled = true;
return;
}
const usedExport = getRelevantCall(
node,
this.namespaceImport,
this.importIdentifiers,
);
if (usedExport && usedExport in debuggableFunctionConfig) {
if (
node.arguments.length <
debuggableFunctionConfig[
usedExport as keyof typeof debuggableFunctionConfig
].maxParams
) {
const debugIdent = getDebugId(path);
if (debugIdent) {
node.arguments.push(t.stringLiteral(debugIdent));
}
}
}
},
},
};
}
Example #10
Source File: add-exports-array.ts From mpflow with MIT License | 4 votes |
/**
* 向配置文件如
* 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))()]),
)
}
})
},
},
},
}
}