UNPKG

effector

Version:

Business logic with ease

997 lines (984 loc) 34.6 kB
'use strict'; const REGION_NAME = '_internalHMRRegion'; function addFileNameIdentifier(addLoc, enableFileName, t, path, state) { if (addLoc && !state.fileNameIdentifier) { const fileName = enableFileName ? stripRoot(state.file.opts.root || '', state.filename || '', false) : ''; const fileNameIdentifier = path.scope.generateUidIdentifier('_effectorFileName'); // babel bug https://github.com/babel/babel/issues/9496 if (path.hub) { const scope = path.hub.getScope(); if (scope) { scope.push({ id: fileNameIdentifier, init: t.stringLiteral(fileName) }); } } state.fileNameIdentifier = fileNameIdentifier; } } function addImport(t, path, method, importNamesMap) { if (importNamesMap[method] !== null) { return importNamesMap[method]; } const programPath = findProgramPath(path); const uid = programPath.scope.generateUidIdentifier(method); const specifier = t.importSpecifier(uid, t.identifier(method)); if (importNamesMap.importDeclaration === null) { const importDeclaration = t.importDeclaration([specifier], t.stringLiteral('effector')); const [importPath] = programPath.unshiftContainer('body', importDeclaration); importNamesMap.importDeclaration = importDeclaration; importNamesMap.importDeclarationPath = importPath; } else { importNamesMap.importDeclaration.specifiers.push(specifier); } importNamesMap[method] = uid.name; return uid.name; } function getImportedName(t, node) { return t.isIdentifier(node.imported) ? node.imported.name : node.imported.value; } function applyMethodParsers(methodParsers, path, state, name) { for (let i = 0; i < methodParsers.length; i++) { const { flag, set, fn } = methodParsers[i]; if (flag && set.has(name)) { fn(path, state, name, findCandidateNameForExpression(path)); } } } function findCandidateNameForExpression(path) { let id; path.find(path => { if (path.isAssignmentExpression()) { id = path.node.left; } else if (path.isObjectProperty()) { id = path.node.key; } else if (path.isVariableDeclarator()) { id = path.node.id; } else if (path.isStatement()) { // we've hit a statement, we should stop crawling up return true; } // we've got an id! no need to continue if (id) return true; return false; }); return id; } function makeTrace(fileNameIdentifier, lineNumber, columnNumber, t) { const fileLineLiteral = t.numericLiteral(lineNumber != null ? lineNumber : -1); const fileColumnLiteral = t.numericLiteral(columnNumber != null ? columnNumber : -1); const fileProperty = property(t, 'file', fileNameIdentifier); const lineProperty = property(t, 'line', fileLineLiteral); const columnProperty = property(t, 'column', fileColumnLiteral); return t.objectExpression([fileProperty, lineProperty, columnProperty]); } function findLocArgs(path) { let loc; let args; path.find(path => { if (path.isCallExpression()) { var _path$node$loc; args = path.node.arguments; loc = (_path$node$loc = path.node.loc) === null || _path$node$loc === void 0 ? void 0 : _path$node$loc.start; return true; } return false; }); return { args, loc }; } function setRestoreNameAfter(path, state, nameNodeId, t, { addLoc, addNames, debugSids }, checkBindingName) { const displayName = nameNodeId ? nameNodeId.name : ''; if (isLocalVariable(path, checkBindingName)) return; const { args, loc } = findLocArgs(path); if (args) { if (!args[0]) return; if (!args[1]) return; const oldConfig = args[2]; const configExpr = args[2] = t.objectExpression([]); const stableID = stringProperty(t, 'sid', generateStableID(state.file.opts.root, state.filename, displayName, loc.line, loc.column, debugSids)); if (oldConfig) { configExpr.properties.push(property(t, 'and', oldConfig)); } if (addLoc) { const locProp = property(t, 'loc', makeTrace(state.fileNameIdentifier, loc.line, loc.column, t)); configExpr.properties.push(locProp); } if (displayName && addNames) { configExpr.properties.push(stringProperty(t, 'name', displayName)); } configExpr.properties.push(stableID); } } function setStoreNameAfter(path, state, nameNodeId, t, { addLoc, addNames, debugSids }, fillFirstArg, checkBindingName) { const displayName = nameNodeId ? nameNodeId.name : ''; if (isLocalVariable(path, checkBindingName)) return; const { args, loc } = findLocArgs(path); if (args) { if (!args[0]) { if (!fillFirstArg) return; args[0] = t.nullLiteral(); } const oldConfig = args[1]; const configExpr = args[1] = t.objectExpression([]); const stableID = stringProperty(t, 'sid', generateStableID(state.file.opts.root, state.filename, displayName, loc.line, loc.column, debugSids)); if (oldConfig) { configExpr.properties.push(property(t, 'and', oldConfig)); } if (addLoc) { const locProp = property(t, 'loc', makeTrace(state.fileNameIdentifier, loc.line, loc.column, t)); configExpr.properties.push(locProp); } if (displayName && addNames) { configExpr.properties.push(stringProperty(t, 'name', displayName)); } configExpr.properties.push(stableID); } } function isLocalVariable(path, checkBindingName) { if (!checkBindingName) return false; const binding = path.scope.getBinding(checkBindingName); if (binding) return binding.kind !== 'module'; return false; } function setConfigForConfMethod(path, state, nameNodeId, t, { addLoc, addNames, debugSids }, singleArgument, checkBindingName, allowEmptyArguments) { const displayName = nameNodeId ? nameNodeId.name : ''; if (isLocalVariable(path, checkBindingName)) return; const { args, loc } = findLocArgs(path); if (args) { if (!args[0] && !allowEmptyArguments) return; const commonArgs = singleArgument ? args[0] : t.arrayExpression(args.slice()); args.length = 0; const configExpr = t.objectExpression([]); const stableID = stringProperty(t, 'sid', generateStableID(state.file.opts.root, state.filename, displayName, loc.line, loc.column, debugSids)); if (addLoc) { const locProp = property(t, 'loc', makeTrace(state.fileNameIdentifier, loc.line, loc.column, t)); configExpr.properties.push(locProp); } if (displayName && addNames) { configExpr.properties.push(stringProperty(t, 'name', displayName)); } configExpr.properties.push(stableID); args[0] = t.objectExpression([property(t, 'and', commonArgs), property(t, 'or', configExpr)]); } } function setEventNameAfter(path, state, nameNodeId, t, { addLoc, addNames, debugSids }, checkBindingName) { const displayName = nameNodeId ? nameNodeId.name : ''; if (isLocalVariable(path, checkBindingName)) return; const { args, loc } = findLocArgs(path); if (args) { const firstArgument = args[0]; if (!firstArgument) { if (displayName) { args[0] = t.stringLiteral(displayName); } } const oldConfig = args[1]; const configExpr = args[firstArgument ? 1 : 0] = t.objectExpression([]); const stableID = stringProperty(t, 'sid', generateStableID(state.file.opts.root, state.filename, displayName, loc.line, loc.column, debugSids)); if (oldConfig) { configExpr.properties.push(property(t, 'and', oldConfig)); } if (addLoc) { const locProp = property(t, 'loc', makeTrace(state.fileNameIdentifier, loc.line, loc.column, t)); configExpr.properties.push(locProp); } if (displayName && addNames) { configExpr.properties.push(stringProperty(t, 'name', displayName)); } configExpr.properties.push(stableID); } } function rememberLocalName(importedName, localName, creatorsList) { for (const creators of creatorsList) { if (creators.has(importedName)) { creators.add(localName); return; } } } function normalizeSource(source, rootPath, state) { let normalizedSource = source; if (normalizedSource.startsWith('.')) { const { resolve, parse } = require('path'); const currentFile = state.filename || ''; const { dir } = parse(currentFile); const resolvedImport = resolve(dir, normalizedSource); normalizedSource = stripRoot(rootPath, resolvedImport, true); } return stripExtension(normalizedSource); } function stripExtension(path) { const { extname } = require('path'); const ext = extname(path); if (ext.length > 0) { path = path.slice(0, -ext.length); } return path; } function stripRoot(babelRoot, fileName, omitFirstSlash) { const { sep, normalize } = require('path'); const rawPath = (fileName || '').replace(babelRoot || '', ''); let normalizedSeq = normalize(rawPath).split(sep); if (omitFirstSlash && normalizedSeq.length > 0 && normalizedSeq[0] === '') { normalizedSeq = normalizedSeq.slice(1); } const normalizedPath = normalizedSeq.join('/'); return normalizedPath; } /** * "foo src/index.js [12,30]" */ function generateStableID(babelRoot, fileName, varName, line, column, debugSids) { const normalizedPath = stripRoot(babelRoot, fileName, false); const appendix = debugSids ? `:${normalizedPath}:${varName}` : ''; return hashCode(`${varName} ${normalizedPath} [${line}, ${column}]`) + appendix; } function hashCode(s) { let h = 0; let i = 0; if (s.length > 0) while (i < s.length) h = (h << 5) - h + s.charCodeAt(i++) | 0; return h.toString(36); } function property(t, field, content) { return t.objectProperty(t.identifier(field), content); } function stringProperty(t, field, value) { return property(t, field, t.stringLiteral(value)); } function findProgramPath(path) { return path.find(path => path.isProgram()); } const defaultFactories = ['@farfetched/core', '@effector/reflect', '@effector/reflect/ssr', '@effector/reflect/scope', 'atomic-router', '@withease/factories', 'effector-action', 'patronum' // there is also custom handling for patronum/{method} imports ]; function normalizeOptions(options) { const defaults = options.noDefaults ? { store: [], event: [], effect: [], domain: [], restore: [], combine: [], sample: [], forward: [], guard: [], attach: [], split: [], createApi: [], merge: [], domainMethods: { store: [], event: [], effect: [], domain: [] }, reactMethods: { createGate: [] }, factories: [] } : { store: ['createStore'], event: ['createEvent'], effect: ['createEffect'], domain: ['createDomain'], restore: ['restore'], combine: ['combine'], sample: ['sample'], forward: ['forward'], guard: ['guard'], attach: ['attach'], split: ['split'], createApi: ['createApi'], merge: ['merge'], domainMethods: { store: ['store', 'createStore'], event: ['event', 'createEvent'], effect: ['effect', 'createEffect'], domain: ['domain', 'createDomain'] }, reactMethods: { createGate: ['createGate'] }, factories: defaultFactories }; const fullConfig = readConfigFlags({ options, properties: { reactSsr: false, transformLegacyDomainMethods: true, forceScope: false, filename: true, stores: true, events: true, effects: true, domains: true, restores: true, combines: true, samples: true, forwards: true, guards: true, attaches: true, splits: true, apis: true, merges: true, gates: true }, result: { importName: new Set(options.importName ? Array.isArray(options.importName) ? options.importName : [options.importName] : ['effector', 'effector/compat', 'effector-root', 'effector-root/compat', 'effector-logger', 'trail/runtime', '@effector/effector']), importReactNames: { ssr: new Set(readReactImportOption(options, 'ssr', ['effector-react/scope', 'effector-react/ssr'])), nossr: new Set(readReactImportOption(options, 'nossr', ['effector-react', 'effector-react/compat'])) }, storeCreators: new Set(options.storeCreators || defaults.store), eventCreators: new Set(options.eventCreators || defaults.event), effectCreators: new Set(options.effectCreators || defaults.effect), domainCreators: new Set(options.domainCreators || defaults.domain), restoreCreators: new Set(options.restoreCreators || defaults.restore), combineCreators: new Set(options.combineCreators || defaults.combine), sampleCreators: new Set(options.sampleCreators || defaults.sample), forwardCreators: new Set(options.forwardCreators || defaults.forward), guardCreators: new Set(options.guardCreators || defaults.guard), attachCreators: new Set(options.attachCreators || defaults.attach), splitCreators: new Set(options.splitCreators || defaults.split), apiCreators: new Set(options.apiCreators || defaults.createApi), mergeCreators: new Set(options.mergeCreators || defaults.merge), domainMethods: readConfigShape(options.domainMethods, defaults.domainMethods), reactMethods: readConfigShape(options.reactMethods, defaults.reactMethods), factories: [...(options.factories || []), ...defaults.factories].map(stripExtension), addLoc: Boolean(options.addLoc), debugSids: Boolean(options.debugSids), forceScope: Boolean(options.forceScope), hmr: options.hmr === 'none' ? false : options.hmr || false, addNames: typeof options.addNames !== 'undefined' ? Boolean(options.addNames) : true } }); const creatorsList = [fullConfig.storeCreators, fullConfig.eventCreators, fullConfig.effectCreators, fullConfig.domainCreators, fullConfig.restoreCreators, fullConfig.combineCreators, fullConfig.sampleCreators, fullConfig.forwardCreators, fullConfig.guardCreators, fullConfig.attachCreators, fullConfig.splitCreators, fullConfig.apiCreators, fullConfig.mergeCreators]; return { fullConfig, creatorsList }; } function readReactImportOption(options, name, defaults) { if (!options || !options.importReactNames) return defaults; const { importReactNames } = options; const value = importReactNames[name]; if (value) { if (Array.isArray(value)) return value;else return [value]; } return defaults; } function readConfigShape(shape = {}, defaults) { const result = {}; for (const key in defaults) { result[key] = readConfigArray(shape[key], defaults[key]); } return result; } function readConfigArray(array, defaults) { return new Set(array || defaults); } function readConfigFlags({ options, properties, result }) { for (const property in properties) { if (property in options) { result[property] = Boolean(options[property]); } else { result[property] = properties[property]; } } return result; } function getMethodParsers(t, smallConfig, { stores, events, effects, domains, restores, combines, samples, forwards, guards, attaches, splits, apis, merges, gates, storeCreators, eventCreators, effectCreators, domainCreators, restoreCreators, combineCreators, sampleCreators, forwardCreators, guardCreators, attachCreators, splitCreators, apiCreators, mergeCreators, domainMethods, reactMethods }) { const methodParsers = [{ flag: stores, set: storeCreators, fn: (path, state, name, id) => setStoreNameAfter(path, state, id, t, smallConfig, false, name) }, { flag: events, set: eventCreators, fn: (path, state, name, id) => setEventNameAfter(path, state, id, t, smallConfig, name) }, { flag: effects, set: effectCreators, fn: (path, state, name, id) => setEventNameAfter(path, state, id, t, smallConfig, name) }, { flag: domains, set: domainCreators, fn: (path, state, name, id) => setEventNameAfter(path, state, id, t, smallConfig, name) }, { flag: restores, set: restoreCreators, fn: (path, state, name, id) => setRestoreNameAfter(path, state, id, t, smallConfig, name) }, { flag: combines, set: combineCreators, fn: (path, state, name, id) => setConfigForConfMethod(path, state, id, t, smallConfig, false, name) }, { flag: samples, set: sampleCreators, fn: (path, state, name, id) => setConfigForConfMethod(path, state, id, t, smallConfig, false, name) }, { flag: forwards, set: forwardCreators, fn: (path, state, name, id) => setConfigForConfMethod(path, state, id, t, smallConfig, true, name) }, { flag: guards, set: guardCreators, fn: (path, state, name, id) => setConfigForConfMethod(path, state, id, t, smallConfig, false, name) }, { flag: attaches, set: attachCreators, fn: (path, state, name, id) => setConfigForConfMethod(path, state, id, t, smallConfig, true, name) }, { flag: splits, set: splitCreators, fn: (path, state, name, id) => setConfigForConfMethod(path, state, null, t, smallConfig, false, name) }, { flag: apis, set: apiCreators, fn: (path, state, name, id) => setConfigForConfMethod(path, state, null, t, smallConfig, false, name) }, { flag: merges, set: mergeCreators, fn: (path, state, name, id) => setStoreNameAfter(path, state, id, t, smallConfig, false, name) }]; const domainMethodParsers = [{ flag: stores, set: domainMethods.store, fn: (path, state, name, id) => setStoreNameAfter(path, state, id, t, smallConfig, false) }, { flag: events, set: domainMethods.event, fn: (path, state, name, id) => setEventNameAfter(path, state, id, t, smallConfig) }, { flag: effects, set: domainMethods.effect, fn: (path, state, name, id) => setEventNameAfter(path, state, id, t, smallConfig) }, { flag: domains, set: domainMethods.domain, fn: (path, state, name, id) => setEventNameAfter(path, state, id, t, smallConfig) }]; const reactMethodParsers = [{ flag: gates, set: reactMethods.createGate, fn: (path, state, name, id) => setConfigForConfMethod(path, state, id, t, smallConfig, false, name, true) }]; return { methodParsers, domainMethodParsers, reactMethodParsers }; } function transformReactHooks(t, name, reactMethodParsers, forceScope, path, state) { applyMethodParsers(reactMethodParsers, path, state, name); transformReactForceScope(t, name, forceScope, path, state); } function transformReactForceScope(t, name, forceScope, path, state) { if (!forceScope || !state.effector_forceScopeSpecifiers.has(name)) return; const binding = path.scope.getBinding(name); if (!binding || !t.isImportSpecifier(binding.path.node)) return; const hookName = getImportedName(t, binding.path.node); switch (hookName) { case 'useEvent': case 'useStore': case 'useUnit': { if (!hasSecondArgument(path)) { pushForceScopeToConfig(t, path); } break; } case 'useList': { if (!hasThirdArgument(path)) { pushForceScopeToConfig(t, path); } break; } case 'useGate': { if (!hasThirdArgument(path)) { // If no props passed if (!hasSecondArgument(path)) { path.node.arguments.push(t.objectExpression([])); } // Add forceScope: true pushForceScopeToConfig(t, path); } break; } case 'useStoreMap': { if (hasSecondArgument(path)) { const config = convertArgumentsToConfig(t, path, ['store', 'fn']); path.node.arguments = [config]; // Add keys: [] config.properties.push(property(t, 'keys', t.arrayExpression())); // Add forceScope: true config.properties.push(property(t, 'forceScope', t.booleanLiteral(true))); } else { const firstArg = path.node.arguments[0]; if (t.isObjectExpression(firstArg) && !hasPropertyInConfig(t, firstArg, 'forceScope')) { firstArg.properties.push(property(t, 'forceScope', t.booleanLiteral(true))); } } break; } } } function hasSecondArgument(path) { return path.node.arguments.length >= 2; } function pushForceScopeToConfig(t, path) { path.node.arguments.push(t.objectExpression([property(t, 'forceScope', t.booleanLiteral(true))])); } function hasThirdArgument(path) { return path.node.arguments.length >= 3; } function convertArgumentsToConfig(t, path, argumentsAsKeys) { const objectProperties = argumentsAsKeys.map((keyName, index) => t.objectProperty(t.identifier(keyName), t.cloneNode(path.node.arguments[index]))); return t.objectExpression(objectProperties); } function hasPropertyInConfig(t, configNode, propertyName) { return configNode.properties.some(property => !t.isSpreadElement(property) && t.isIdentifier(property.key) && property.key.name === propertyName); } function transformFactory(t, path, state, factoriesUsed, debugSids, addLoc, addNames, factoryTemplate) { const node = path.node; const isTaggedTemplate = t.isTaggedTemplateExpression(node); const callee = isTaggedTemplate ? node.tag : node.callee; if (!t.isIdentifier(callee)) return; const name = callee.name; if (!factoriesUsed || node.effector_isFactory || !state.effector_factoryMap.has(name)) { return; } const withFactoryImportName = addImport(t, path, 'withFactory', state.effector_importNames); const { importedName } = state.effector_factoryMap.get(name); node.effector_isFactory = true; const idNode = findCandidateNameForExpression(path); const resultName = idNode ? idNode.name : ''; const nodeFinder = isTaggedTemplate ? t.isTaggedTemplateExpression : t.isCallExpression; let loc; path.find(path => { if (nodeFinder(path.node)) { if (path.node.loc) { loc = path.node.loc.start; } return true; } return false; }); const sid = generateStableID(state.file.opts.root, state.filename, resultName, loc.line, loc.column, debugSids); const factoryConfig = { SID: JSON.stringify(sid), FN: node, FACTORY: withFactoryImportName }; if (addLoc || addNames) { factoryConfig.NAME = JSON.stringify(!resultName || resultName === '' ? 'none' : resultName); factoryConfig.METHOD = JSON.stringify(importedName); } if (addLoc) { factoryConfig.LOC = makeTrace(state.fileNameIdentifier, loc.line, loc.column, t); } path.replaceWith(factoryTemplate(factoryConfig)); } function processLegacyDomainHooks(t, domainMethodParsers, transformLegacyDomainMethods, path, state) { if (transformLegacyDomainMethods && t.isMemberExpression(path.node.callee) && 'name' in path.node.callee.property) { applyMethodParsers(domainMethodParsers, path, state, path.node.callee.property.name); } } const DEFAULT_WATCHED_CALLS = ['map', 'filter', 'filterMap', 'subscribe', 'on', 'watch', 'reset', 'prepend']; const SUPPORTED_NODES = ['FunctionDeclaration', 'ArrowFunctionExpression', 'ExportDefaultDeclaration', 'ClassDeclaration']; function transformHmr(t, path, factories, importNamesMap, hmrMode, createHMRRegion, createWithRegion, createHotCode) { const watchedFactories = new Set(DEFAULT_WATCHED_CALLS); path.traverse({ ImportDeclaration(declaration) { const source = declaration.node.source.value; const specifiers = declaration.node.specifiers.map(specifier => specifier.local.name); if (!['effector', ...factories].includes(source)) { return; } for (const specifier of specifiers) { watchedFactories.add(specifier); } if (source !== 'effector') { return; } }, CallExpression(path) { applyHMRTransform(t, path, path.node.callee, watchedFactories, importNamesMap, hmrMode, createHMRRegion, createWithRegion, createHotCode); }, TaggedTemplateExpression(path) { applyHMRTransform(t, path, path.node.tag, watchedFactories, importNamesMap, hmrMode, createHMRRegion, createWithRegion, createHotCode); } }); } function applyHMRTransform(t, path, callee, watchedFactories, importNamesMap, hmrMode, createHMRRegion, createWithRegion, createHotCode) { if (!isSupportHMR(path, callee, watchedFactories)) { return; } if (!importNamesMap.hmrRegionInserted) { const createNodeName = addImport(t, path, 'createNode', importNamesMap); const regionNode = createHMRRegion({ CREATE_NODE: createNodeName, REGION_NAME }); /** guaranteed to be initialized after addImport call */ importNamesMap.importDeclarationPath.insertAfter(regionNode); importNamesMap.hmrRegionInserted = true; } if (!importNamesMap.hmrCodeInserted) { const hotCode = createHotCode(importNamesMap, path, hmrMode); const programPath = findProgramPath(path); programPath.pushContainer('body', hotCode); importNamesMap.hmrCodeInserted = true; } path.replaceWith(createWithRegion({ WITH_REGION: addImport(t, path, 'withRegion', importNamesMap), REGION_NAME, FN: path.node })); } function isSupportHMR(path, callee, watchedFactories) { var _property; /** calls like `[foo]()`, who the hell even does that? */ if (!('name' in callee) && !(callee.property && callee.property.name)) { return false; } const name = callee.name || ((_property = callee.property) === null || _property === void 0 ? void 0 : _property.name); if (!watchedFactories.has(name)) { return false; } return !path.findParent(parent => parent && SUPPORTED_NODES.includes(parent.node.type)); } function createFactoryTemplate(template, addLoc, addNames) { let factoryTemplate = null; return args => { if (!factoryTemplate) { factoryTemplate = template(addLoc ? 'FACTORY({sid: SID,fn:()=>FN,name:NAME,method:METHOD,loc:LOC})' : addNames ? 'FACTORY({sid: SID,fn:()=>FN,name:NAME,method:METHOD})' : 'FACTORY({sid: SID,fn:()=>FN})'); } return factoryTemplate(args); }; } function createWithRegionTemplate(template) { let withRegionTemplate = null; return args => { if (!withRegionTemplate) { withRegionTemplate = template('WITH_REGION(REGION_NAME, () => FN)'); } return withRegionTemplate(args); }; } function createHMRRegionTemplate(template) { let hmrRegionTemplate = null; return args => { if (!hmrRegionTemplate) { hmrRegionTemplate = template('const REGION_NAME = CREATE_NODE({regional: true})'); } return hmrRegionTemplate(args); }; } function createHotCodeTemplate(template, t) { let hotProperty; let hotCodeTemplate = null; return (importNamesMap, declaration, hmrMode) => { if (!hotProperty) { hotProperty = template.expression.ast(hmrMode === 'cjs' ? 'module.hot' : '(import.meta.hot || import.meta.webpackHot)'); } if (!hotCodeTemplate) { hotCodeTemplate = template(` if (HOT_PROPERTY) { HOT_PROPERTY.dispose(() => CLEAR_NODE(REGION_NAME)); } else { console.warn('[effector hmr] HMR is not available in current environment.'); } `); } return hotCodeTemplate({ HOT_PROPERTY: hotProperty, CLEAR_NODE: addImport(t, declaration, 'clearNode', importNamesMap), REGION_NAME }); }; } function effectorBabelPlugin(babel, options = {}) { const { fullConfig, creatorsList } = normalizeOptions(options); const { addNames, addLoc, debugSids, transformLegacyDomainMethods, hmr, filename: enableFileName, reactMethods, factories, importName, importReactNames, reactSsr, forceScope } = fullConfig; if (reactSsr) { console.error('[effector/babel-plugin]: reactSsr option is deprecated, use imports from "effector-react" without aliases or /scope'); } const factoriesUsed = factories.length > 0; const hasRelativeFactories = factories.some(fab => fab.startsWith('./') || fab.startsWith('../')); const smallConfig = { addLoc, addNames, debugSids }; const { types: t, template } = babel; const factoryTemplate = createFactoryTemplate(template, addLoc, addNames); const hmrRegionTemplate = createHMRRegionTemplate(template); const withRegionTemplate = createWithRegionTemplate(template); const hotCodeTemplate = createHotCodeTemplate(template, t); const { methodParsers, reactMethodParsers, domainMethodParsers } = getMethodParsers(t, smallConfig, fullConfig); const importVisitor = { ImportDeclaration(path, state) { const source = path.node.source.value; const specifiers = path.node.specifiers; if (importName.has(source)) { for (let i = 0; i < specifiers.length; i++) { const s = specifiers[i]; if (!t.isImportSpecifier(s)) continue; const importedName = getImportedName(t, s); const localName = s.local.name; if (importedName === localName) continue; rememberLocalName(importedName, localName, creatorsList); } } else if (importReactNames.ssr.has(source) || importReactNames.nossr.has(source)) { for (let i = 0; i < specifiers.length; i++) { const s = specifiers[i]; if (!t.isImportSpecifier(s)) continue; const importedName = getImportedName(t, s); const localName = s.local.name; // effector-react/scope already forced scope if (importReactNames.nossr.has(source)) { this.effector_forceScopeSpecifiers.add(localName); } if (importedName === localName) continue; if (reactMethods.createGate.has(importedName)) { reactMethods.createGate.add(localName); } } } else { for (let i = 0; i < specifiers.length; i++) { const s = specifiers[i]; if (t.isImportNamespaceSpecifier(s)) continue; const localName = s.local.name; if (creatorsList.some(set => set.has(localName))) { this.effector_ignoredImports.add(localName); } } } if (reactSsr) { if (source === 'effector-react' || source === 'effector-react/compat') { path.node.source.value = 'effector-react/scope'; } } if (factoriesUsed) { const rootPath = state.file.opts.root || ''; if (!this.effector_factoryPaths) { if (hasRelativeFactories) { const { resolve } = require('path'); this.effector_factoryPaths = factories.map(fab => { if (fab.startsWith('./') || fab.startsWith('../')) { const resolvedFab = resolve(rootPath, fab); return stripRoot(rootPath, resolvedFab, true); } return fab; }); } else { this.effector_factoryPaths = factories; } } const normalizedSource = normalizeSource(source, rootPath, state); if (this.effector_factoryPaths.includes(normalizedSource) || // custom handling for patronum/{method} imports normalizedSource.startsWith('patronum/')) { this.effector_needFactoryImport = true; for (let i = 0; i < specifiers.length; i++) { const s = specifiers[i]; if (t.isImportNamespaceSpecifier(s)) continue; const importedName = t.isImportDefaultSpecifier(s) ? 'default' : getImportedName(t, s); const localName = s.local.name; this.effector_factoryMap.set(localName, { localName, importedName, source: normalizedSource }); } } } } }; const plugin = { name: 'effector/babel-plugin', pre() { this.effector_ignoredImports = new Set(); this.effector_forceScopeSpecifiers = new Set(); this.effector_factoryMap = new Map(); this.effector_importNames = { withFactory: null, clearNode: null, createNode: null, withRegion: null, importDeclaration: null, importDeclarationPath: null, hmrRegionInserted: false, hmrCodeInserted: false }; }, post() { this.effector_ignoredImports.clear(); this.effector_factoryMap.clear(); this.effector_needFactoryImport = false; this.effector_importNames = { withFactory: null, clearNode: null, createNode: null, withRegion: null, importDeclaration: null, importDeclarationPath: null, hmrRegionInserted: false, hmrCodeInserted: false }; if (this.effector_factoryPaths) { delete this.effector_factoryPaths; } }, visitor: { Program: { enter(path, state) { if (hmr) { var _state$file$opts$call; const hmrMode = hmr === 'cjs' ? 'cjs' : hmr === 'es' ? 'es' : !!((_state$file$opts$call = state.file.opts.caller) !== null && _state$file$opts$call !== void 0 && _state$file$opts$call.supportsStaticESM) ? 'es' : 'cjs'; transformHmr(t, path, factories, this.effector_importNames, hmrMode, hmrRegionTemplate, withRegionTemplate, hotCodeTemplate); } path.traverse(importVisitor, state); } }, CallExpression(path, state) { addFileNameIdentifier(addLoc, enableFileName, t, path, state); processLegacyDomainHooks(t, domainMethodParsers, transformLegacyDomainMethods, path, state); const name = getNonIgnoredCalee(t, path, state); if (name) { transformReactHooks(t, name, reactMethodParsers, forceScope, path, state); processEffectorCommonMethods(name, methodParsers, path, state); } transformFactory(t, path, state, factoriesUsed, debugSids, addLoc, addNames, factoryTemplate); }, TaggedTemplateExpression(path, state) { addFileNameIdentifier(addLoc, enableFileName, t, path, state); transformFactory(t, path, state, factoriesUsed, debugSids, addLoc, addNames, factoryTemplate); } } }; return plugin; } function getNonIgnoredCalee(t, path, state) { if (!t.isIdentifier(path.node.callee)) return null; const name = path.node.callee.name; if (state.effector_ignoredImports.has(name)) return null; return name; } function processEffectorCommonMethods(name, methodParsers, path, state) { applyMethodParsers(methodParsers, path, state, name); } module.exports = effectorBabelPlugin; //# sourceMappingURL=babel-plugin.js.map