diff --git a/.flowconfig b/.flowconfig index 2fa60fe..a2e0238 100644 --- a/.flowconfig +++ b/.flowconfig @@ -2,7 +2,8 @@ .*/node_modules/.* .*/tests/samples/.* .*/lib/.* -.*/src/extractors.js +.*/src/definitions.js +.*/src/declarations.js [libs] declarations/ diff --git a/src/collector.js b/src/collector.js index d003107..f21b4d8 100644 --- a/src/collector.js +++ b/src/collector.js @@ -4,7 +4,9 @@ import type {Node} from '@babel/types'; import globals from './globals'; // $FlowFixMe -import * as extractors from './extractors'; +import definitionGroup from './definitions'; +// $FlowFixMe +import declarationGroup from './declarations'; import Module from './module'; import Scope from './scope'; import CircularList from './list'; @@ -71,7 +73,7 @@ export default class Collector { const scope = this._global.extend(module); - this._freestyle(extractors.declaration, ast.program, scope, []); + this._freestyle(declarationGroup, ast.program, scope, []); this._modules.set(path, module); @@ -277,7 +279,7 @@ export default class Collector { invariant(result.type === 'declaration' || result.type === 'template'); - this._freestyle(extractors.definition, result.node, scope, tmplParams); + this._freestyle(definitionGroup, result.node, scope, tmplParams); while ((result = scope.query(name, params)).type !== 'definition') { invariant(result.type !== 'external'); diff --git a/src/declarations.js b/src/declarations.js new file mode 100644 index 0000000..d0c3cf8 --- /dev/null +++ b/src/declarations.js @@ -0,0 +1,229 @@ +import * as t from '@babel/types'; + +import {declare, external, provide, enter, exit} from './commands'; +import {invariant} from './utils'; + +type E = Generator; + +export default { + entries: [ + // Blocks. + 'Program', + 'BlockStatement', + // Imports. + 'ImportDeclaration', + 'VariableDeclarator', + // Exports. + 'ExportNamedDeclaration', + 'ExportDefaultDeclaration', + ], + + /* + * Blocks. + */ + + * Program(node: t.Program): E { + yield node.body; + }, + + * BlockStatement(node: t.BlockStatement): E { + yield enter(); + yield node.body; + yield exit(); + }, + + /* + * Imports. + * + * TODO: warning about "import typeof". + * TODO: support form "import *". + */ + + * ImportDeclaration(node: t.ImportDeclaration): E { + const specifiers = yield node.specifiers; + const path = yield node.source; + + for (const specifier of specifiers) { + specifier.path = path; + + yield external(specifier); + } + }, + + * ImportDefaultSpecifier(node: t.ImportDefaultSpecifier): E { + return { + local: yield node.local, + imported: null, + }; + }, + + * ImportSpecifier(node: t.ImportSpecifier): E { + return { + local: yield node.local, + imported: yield node.imported, + }; + }, + + * VariableDeclarator(node: t.VariableDeclarator): E { + const path = extractRequire(node.init); + + if (!path) { + return null; + } + + let specifiers = yield node.id; + + if (typeof specifiers === 'string') { + specifiers = [{ + local: specifiers, + imported: null, + }]; + } + + for (const specifier of specifiers) { + specifier.path = path; + + yield external(specifier); + } + }, + + * ObjectPattern(node: t.ObjectPattern): E { + return yield node.properties; + }, + + * ObjectProperty(node: t.ObjectProperty): E { + const key = yield node.key; + + // TODO: different roots. + if (node.value.type !== 'Identifier') { + return null; + } + + //invariant(node.value.type === 'Identifier'); + + const value = yield node.value; + + return { + local: value, + imported: key, + }; + }, + + /* + * Exports. + * + * TODO: support "export from" form. + * TODO: support commonjs. + */ + + * ExportDefaultDeclaration(node: t.ExportDefaultDeclaration): E { + const reference = yield node.declaration; + + if (reference) { + yield provide(null, reference); + } + }, + + * ExportNamedDeclaration(node: t.ExportNamedDeclaration): E { + if (!node.declaration) { + yield node.specifiers; + return; + } + + const reference = yield node.declaration; + + if (reference) { + yield provide(reference); + } + }, + + * ExportSpecifier(node: t.ExportSpecifier): E { + const reference = yield node.local; + let name = yield node.exported; + + if (name === 'default') { + name = null; + } + + yield provide(name, reference); + }, + + /* + * Declarations. + */ + + * TypeAlias(node: t.TypeAlias): E { + const name = yield node.id; + const params = node.typeParameters && (yield node.typeParameters); + + yield declare(name, node, params); + + return name; + }, + + * InterfaceDeclaration(node: t.InterfaceDeclaration): E { + const name = yield node.id; + const params = node.typeParameters && (yield node.typeParameters); + + yield declare(name, node, params); + + return name; + }, + + * ClassDeclaration(node: t.ClassDeclaration): E { + const name = yield node.id; + const params = node.typeParameters && (yield node.typeParameters); + + // TODO: do it only for "all"-mode. + const body = node.body; + yield body.body.filter(n => t.isClassMethod(n)); + + yield declare(name, node, params); + + return name; + }, + + * TypeParameterDeclaration(node: t.TypeParameterDeclaration): E { + return yield node.params; + }, + + * TypeParameter(node: t.TypeParameter): E { + return { + name: node.name, + default: node.default ? yield node.default : null, + }; + }, + + /* + * Utility. + */ + + * StringLiteral(node: t.StringLiteral): E { + return node.value; + }, + + * Identifier(node: t.Identifier): E { + return node.name; + }, +}; + +function extractRequire(node) { + // XXX: refactor it! + + // TODO: use `t.*` helpers. + const ok = node && + node.type === 'CallExpression' && + node.callee.type === 'Identifier' && + node.callee.name === 'require'; + + if (!ok) { + return null; + } + + const argument = node.arguments[0]; + + // TODO: warning about dynamic imports. + invariant(t.isStringLiteral(argument)); + + return argument.value; +} diff --git a/src/extractors.js b/src/definitions.js similarity index 62% rename from src/extractors.js rename to src/definitions.js index 84bff47..4fe0095 100644 --- a/src/extractors.js +++ b/src/definitions.js @@ -1,12 +1,12 @@ import * as t from '@babel/types'; -import {declare, define, external, provide, query, enter, exit, namespace} from './commands'; +import {define, query, namespace} from './commands'; import {invariant, partition} from './utils'; import type {Schema} from './schema'; type E = Generator; -export const definition = { +export default { entries: [ 'TypeAlias', 'InterfaceDeclaration', @@ -247,208 +247,6 @@ export const definition = { }, }; -export const declaration = { - entries: [ - // Blocks. - 'Program', - 'BlockStatement', - // Imports. - 'ImportDeclaration', - 'VariableDeclarator', - // Exports. - 'ExportNamedDeclaration', - 'ExportDefaultDeclaration', - ], - - /* - * Blocks. - */ - - * Program(node: t.Program): E { - yield node.body; - }, - - * BlockStatement(node: t.BlockStatement): E { - yield enter(); - yield node.body; - yield exit(); - }, - - /* - * Imports. - * - * TODO: warning about "import typeof". - * TODO: support form "import *". - */ - - * ImportDeclaration(node: t.ImportDeclaration): E { - const specifiers = yield node.specifiers; - const path = yield node.source; - - for (const specifier of specifiers) { - specifier.path = path; - - yield external(specifier); - } - }, - - * ImportDefaultSpecifier(node: t.ImportDefaultSpecifier): E { - return { - local: yield node.local, - imported: null, - }; - }, - - * ImportSpecifier(node: t.ImportSpecifier): E { - return { - local: yield node.local, - imported: yield node.imported, - }; - }, - - * VariableDeclarator(node: t.VariableDeclarator): E { - const path = extractRequire(node.init); - - if (!path) { - return null; - } - - let specifiers = yield node.id; - - if (typeof specifiers === 'string') { - specifiers = [{ - local: specifiers, - imported: null, - }]; - } - - for (const specifier of specifiers) { - specifier.path = path; - - yield external(specifier); - } - }, - - * ObjectPattern(node: t.ObjectPattern): E { - return yield node.properties; - }, - - * ObjectProperty(node: t.ObjectProperty): E { - const key = yield node.key; - - // TODO: different roots. - if (node.value.type !== 'Identifier') { - return null; - } - - //invariant(node.value.type === 'Identifier'); - - const value = yield node.value; - - return { - local: value, - imported: key, - }; - }, - - /* - * Exports. - * - * TODO: support "export from" form. - * TODO: support commonjs. - */ - - * ExportDefaultDeclaration(node: t.ExportDefaultDeclaration): E { - const reference = yield node.declaration; - - if (reference) { - yield provide(null, reference); - } - }, - - * ExportNamedDeclaration(node: t.ExportNamedDeclaration): E { - if (!node.declaration) { - yield node.specifiers; - return; - } - - const reference = yield node.declaration; - - if (reference) { - yield provide(reference); - } - }, - - * ExportSpecifier(node: t.ExportSpecifier): E { - const reference = yield node.local; - let name = yield node.exported; - - if (name === 'default') { - name = null; - } - - yield provide(name, reference); - }, - - /* - * Declarations. - */ - - * TypeAlias(node: t.TypeAlias): E { - const name = yield node.id; - const params = node.typeParameters && (yield node.typeParameters); - - yield declare(name, node, params); - - return name; - }, - - * InterfaceDeclaration(node: t.InterfaceDeclaration): E { - const name = yield node.id; - const params = node.typeParameters && (yield node.typeParameters); - - yield declare(name, node, params); - - return name; - }, - - * ClassDeclaration(node: t.ClassDeclaration): E { - const name = yield node.id; - const params = node.typeParameters && (yield node.typeParameters); - - // TODO: do it only for "all"-mode. - const body = node.body; - yield body.body.filter(n => t.isClassMethod(n)); - - yield declare(name, node, params); - - return name; - }, - - * TypeParameterDeclaration(node: t.TypeParameterDeclaration): E { - return yield node.params; - }, - - * TypeParameter(node: t.TypeParameter): E { - return { - name: node.name, - default: node.default ? yield node.default : null, - }; - }, - - /* - * Utility. - */ - - * StringLiteral(node: t.StringLiteral): E { - return node.value; - }, - - * Identifier(node: t.Identifier): E { - return node.name; - }, -}; - function* extractLastPragma(comments: t.Comment[]): ?string { const pragmas = (yield comments).filter(Boolean); @@ -482,27 +280,6 @@ function* extractProperty(prop, value) { }; } -function extractRequire(node) { - // XXX: refactor it! - - // TODO: use `t.*` helpers. - const ok = node && - node.type === 'CallExpression' && - node.callee.type === 'Identifier' && - node.callee.name === 'require'; - - if (!ok) { - return null; - } - - const argument = node.arguments[0]; - - // TODO: warning about dynamic imports. - invariant(t.isStringLiteral(argument)); - - return argument.value; -} - function parsePragma(pragma) { let [type, arg] = pragma.split(/\s+/); @@ -580,7 +357,7 @@ function makeFullname(schema) { return `${schema.namespace}.${schema.name}`; } -function mergeSchemas(schemas: Schemas): Schema { +function mergeSchemas(schemas: Schema[]): Schema { const map = new Map; // TODO: overriding?