Website Structure

This commit is contained in:
supalerk-ar66 2026-01-13 10:46:40 +07:00
parent 62812f2090
commit 71f0676a62
22365 changed files with 4265753 additions and 791 deletions

View file

@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2023-2025 SIA Joom
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,279 @@
# parse-imports-exports
[![NPM version][npm-image]][npm-url]
[![minzipped size][size-image]][size-url]
[![code style: prettier][prettier-image]][prettier-url]
[![Conventional Commits][conventional-commits-image]][conventional-commits-url]
[![License MIT][license-image]][license-url]
Fast and easy parser for declarations of [import](https://tc39.es/ecma262/#prod-ImportDeclaration)
and [export](https://tc39.es/ecma262/#prod-ExportDeclaration) from the ECMAScript standard,
with [TypeScript](https://www.typescriptlang.org/docs/handbook/2/modules.html) syntax support.
`parse-imports-exports` works for syntactically correct, well-formatted code (for example, by [prettier][prettier-url]).
Single-line and multi-line ECMAScript comments in `import`/`export` statements are supported.
## Basic example
Imagine a module with the following content:
```ts
/**
* Imports.
*/
// {Qux: [{start: 17, end: 58, names: {baz: {by: 'foo'}}, types: {Bar: {}}}]}
import {foo as baz, type Bar} from 'Qux';
// {Qux: [{start: 80, end: 112, namespace: 'foo', default: 'Foo'}]}
import Foo, * as foo from 'Qux';
// {Qux: [{start: 114, end: 127}]}
const foo = await import('Qux');
// {Qux: [{start: 128, end, 134}]}
const foo = require('Qux');
// {Qux: [{start: 142, end: 175, names: {Baz: {by: 'Foo'}, Bar: {}}}]}
import type {Foo as Baz, Bar} from 'Qux';
// {Qux: [{start: 201, end: 233, namespace: 'Foo'}]}
import type * as Foo from 'Qux';
// {Qux: [{start: 137, end: 141}]}
type Foo = typeof import('Qux');
/**
* Reexports.
*/
// {Qux: [{start: 254, end: 295, names: {baz: {by: 'foo'}}, types: {Bar: {}}}]}
export {foo as baz, type Bar} from 'Qux';
// {Qux: [{start: 319, end: 346, namespace: 'foo'}]}
export * as foo from 'Qux';
// {Qux: [{start: 365, end: 385}]}
export * from 'Qux';
// {Qux: [{start: 409, end: 450, names: {Baz: {by: 'Foo'}, Bar: {}}}]}
export type {Foo as Baz, Bar} from 'Qux';
// {Qux: [{start: 478, end: 510, namespace: 'Foo'}]}
export type * as Foo from 'Qux';
// {Qux: [{start: 533, end: 558}]}
export type * from 'Qux';
/**
* Exports.
*/
// {start: 578, end: 596}
export default 42;
// [{start: 614, end: 644, names: {baz: {by: 'foo'}}, types: {Bar: {}}}]
export {foo as baz, type Bar};
// {foo: {start: 668, end: 689, kind: 'const'}}
export const foo = 2;
// [{start: 711, end: 741, names: {Baz: {by: 'Foo'}, Bar: {}}}]
export type {Foo as Baz, Bar};
// {T: {start: 758, end: 781}}
export type T = number;
// {I: [{start: 803, end: 836}]}
export interface I {
foo: number;
}
// {N: [{start: 858, end: 891}]}
export namespace N {
foo: number;
}
// {start: 901, end: 915}
module.exports = 42;
// {foo: {start: 917, end: 931, startsWithModule: true}}
module.exports.foo = 2;
```
Let its content be stored as a string in the variable `source`.
Then it can be parsed like this:
```ts
import {parseImportsExports} from 'parse-imports-exports';
const importsExports = parseImportsExports(source);
// Now `importsExports` has the following shape (the `start` and `end` indices, which indicate
// the beginning and end of the corresponding statement in the source, may differ):
const importsExportsShape = {
/**
* Imports.
*/
// import {foo as baz, type Bar} from 'Qux';
namedImports: {Qux: [{start: 17, end: 58, names: {baz: {by: 'foo'}}, types: {Bar: {}}}]},
// import Foo, * as foo from 'Qux';
namespaceImports: {Qux: [{start: 80, end: 112, namespace: 'foo', default: 'Foo'}]},
// const foo = await import('Qux');
dynamicImports: {Qux: [{start: 114, end: 127}]},
// const foo = require('Qux');
requires: {Qux: [{start: 128, end: 134}]},
// import type {Foo as Baz, Bar} from 'Qux';
typeNamedImports: {Qux: [{start: 142, end: 175, names: {Baz: {by: 'Foo'}, Bar: {}}}]},
// import type * as Foo from 'Qux';
typeNamespaceImports: {Qux: [{start: 201, end: 233, namespace: 'Foo'}]},
// type Foo = typeof import('Qux');
typeDynamicImports: {Qux: [{start: 137, end: 141}]},
/**
* Reexports.
*/
// export {foo as baz, type Bar} from 'Qux';
namedReexports: {Qux: [{start: 254, end: 295, names: {baz: {by: 'foo'}}, types: {Bar: {}}}]},
// export * as foo from 'Qux';
namespaceReexports: {Qux: [{start: 319, end: 346, namespace: 'foo'}]},
// export * from 'Qux';
starReexports: {Qux: [{start: 365, end: 385}]},
// export type {Foo as Baz, Bar} from 'Qux';
typeNamedReexports: {Qux: [{start: 409, end: 450, names: {Baz: {by: 'Foo'}, Bar: {}}}]},
// export type * as Foo from 'Qux';
typeNamespaceReexports: {Qux: [{start: 478, end: 510, namespace: 'Foo'}]},
// export type * from 'Qux';
typeStarReexports: {Qux: [{start: 533, end: 558}]},
/**
* Exports.
*/
// export default 42;
defaultExport: {start: 578, end: 596},
// export {foo as baz, type Bar};
namedExports: [{start: 614, end: 644, names: {baz: {by: 'foo'}}, types: {Bar: {}}}],
// export const foo = 2;
declarationExports: {foo: {start: 668, end: 689, kind: 'const'}},
// export type {Foo as Baz, Bar};
typeNamedExports: [{start: 711, end: 741, names: {Baz: {by: 'Foo'}, Bar: {}}}],
// export type T = number;
typeExports: {T: {start: 758, end: 781}},
// export interface I {foo: number};
interfaceExports: {I: [{start: 803, end: 836}]},
// export namespace N {foo: number};
namespaceExports: {N: [{start: 858, end: 891}]},
// module.exports = 42;
commonJsNamespaceExport: {start, end},
// module.exports.foo = 2;
commonJsExports: {foo: {start, end, startsWithModule: true}},
};
```
## Install
Requires [node](https://nodejs.org/en/) version 10 or higher:
```sh
npm install parse-imports-exports
```
`parse-imports-exports` works in any environment that supports ES2018
(because package uses [RegExp Named Capture Groups](https://github.com/tc39/proposal-regexp-named-groups)).
## API
`parse-imports-exports` exports one runtime value — the `parseImportsExports` function:
```ts
import {parseImportsExports} from 'parse-imports-exports';
import type {ImportsExports, Options} from 'parse-imports-exports';
const importsExports: ImportsExports = parseImportsExports('some source code (as string)');
// or with optional options:
const importsExports = parseImportsExports('some source code (as string)', options);
// all option fields are optional boolean with default `false` value
const options: Options = {
/**
* If `true`, then we ignore `module.exports = ...`/`(module.)exports.foo = ...` expressions
* during parsing (maybe a little faster).
* By default (if `false` or skipped option), `module.exports = ...`/`(module.)exports.foo = ...`
* expressions are parsed.
*/
ignoreCommonJsExports: false;
/**
* If `true`, then we ignore `import(...)` expressions during parsing (maybe a little faster).
* By default (if `false` or skipped option), `import(...)` expressions are parsed.
*/
ignoreDynamicImports: false;
/**
* If `true`, then we ignore regular expression literals (`/.../`)
* during parsing (maybe a little faster).
* By default (if `false` or skipped option), regular expression literals are parsed.
*/
ignoreRegexpLiterals: false;
/**
* If `true`, then we ignore `require(...)` expressions during parsing (maybe a little faster).
* By default (if `false` or skipped option), `require(...)` expressions are parsed.
*/
ignoreRequires: false;
/**
* If `true`, then we ignore string literals during parsing (maybe a little faster).
* By default (if `false` or skipped option), string literals are parsed, that is,
* the text inside them cannot be interpreted as another expression.
*/
ignoreStringLiterals: false;
};
```
`parse-imports-exports` also exports types included in the API:
```ts
export type {
/**
* Parsed JSON presentation of imports, exports and reexports of ECMAScript/TypeScript module.
*/
ImportsExports,
/**
* Kind of exported declaration.
*/
Kind,
/**
* Options of `parseImportsExports` function.
*/
Options,
};
```
## License
[MIT][license-url]
[conventional-commits-image]: https://img.shields.io/badge/Conventional_Commits-1.0.0-yellow.svg 'The Conventional Commits specification'
[conventional-commits-url]: https://www.conventionalcommits.org/en/v1.0.0/
[license-image]: https://img.shields.io/badge/license-MIT-blue.svg 'The MIT License'
[license-url]: LICENSE
[npm-image]: https://img.shields.io/npm/v/parse-imports-exports.svg 'parse-imports-exports'
[npm-url]: https://www.npmjs.com/package/parse-imports-exports
[prettier-image]: https://img.shields.io/badge/code_style-prettier-ff69b4.svg 'Prettier code formatter'
[prettier-url]: https://prettier.io/
[size-image]: https://img.shields.io/bundlephobia/minzip/parse-imports-exports 'parse-imports-exports'
[size-url]: https://bundlephobia.com/package/parse-imports-exports

View file

@ -0,0 +1,33 @@
'use strict';
exports.onBacktickError = undefined;
exports.onBacktickParse = undefined;
const { addError } = require('./utils.cjs');
/**
* Adds error of parsing string literal started with backtick.
*/
const onBacktickError = exports.onBacktickError = (importsExports, _source, { start, end }) => addError(importsExports, 'Cannot find end of string literal started with backtick', start, end);
/**
* Parses string literal started with backtick.
*/
const onBacktickParse = exports.onBacktickParse = (importsExports, source, parsedToken) => {
var depth = 0;
var index = parsedToken.end;
for (; index < source.length; index += 1) {
const char = source[index];
if (char === '$') {
if (source[index + 1] === '{' && source[index - 1] !== '\\') {
depth += 1;
index += 1;
}
}
else if (char === '}') {
if (depth !== 0) {
depth -= 1;
}
}
else if (depth === 0 && char === '`' && source[index - 1] !== '\\') {
return index + 1;
}
}
return addError(importsExports, 'Cannot find end of nested string literal started with backtick', parsedToken.start, source.length);
};

View file

@ -0,0 +1,30 @@
import { addError } from './utils.js';
/**
* Adds error of parsing string literal started with backtick.
*/
export const onBacktickError = (importsExports, _source, { start, end }) => addError(importsExports, 'Cannot find end of string literal started with backtick', start, end);
/**
* Parses string literal started with backtick.
*/
export const onBacktickParse = (importsExports, source, parsedToken) => {
var depth = 0;
var index = parsedToken.end;
for (; index < source.length; index += 1) {
const char = source[index];
if (char === '$') {
if (source[index + 1] === '{' && source[index - 1] !== '\\') {
depth += 1;
index += 1;
}
}
else if (char === '}') {
if (depth !== 0) {
depth -= 1;
}
}
else if (depth === 0 && char === '`' && source[index - 1] !== '\\') {
return index + 1;
}
}
return addError(importsExports, 'Cannot find end of nested string literal started with backtick', parsedToken.start, source.length);
};

View file

@ -0,0 +1,50 @@
'use strict';
exports.onCommonJsExportError = undefined;
exports.onCommonJsExportParse = undefined;
const { parseIdentifier } = require('./partParsers.cjs');
const { addError, getPosition, stripComments } = require('./utils.cjs');
/**
* Adds error of parsing `module.exports = ...`/`(module.)exports.foo = ...` statement.
*/
const onCommonJsExportError = exports.onCommonJsExportError = (importsExports, source, { start, end, token }) => addError(importsExports, `Cannot find end (equal sign) of \`${token[0] === 'm' ? 'module.' : ''}exports${source[end] === '.' ? '.' : ''}... = ...\` statement`, start, end);
/**
* Parses `module.exports = ...`/`(module.)exports.foo = ...` statement.
*/
const onCommonJsExportParse = exports.onCommonJsExportParse = (importsExports, source, { start, end: unparsedStart, comments, token }, { start: unparsedEnd, end }) => {
var unparsed = stripComments(source, unparsedStart, unparsedEnd, comments).trim();
const startsWithModule = token[0] === 'm';
if (unparsed[0] === '.') {
unparsed = unparsed.slice(1).trim();
const nameIndex = parseIdentifier(unparsed);
if (nameIndex === 0) {
return addError(importsExports, `Cannot parse identifier of \`${token}.... = ...\` statement`, start, end);
}
const name = unparsed.slice(0, nameIndex);
let { commonJsExports } = importsExports;
if (commonJsExports === undefined) {
importsExports.commonJsExports = commonJsExports = { __proto__: null };
}
else if (name in commonJsExports) {
const firstExport = commonJsExports[name];
let isTranspilerExport = false;
if (startsWithModule === false && firstExport.startsWithModule === undefined) {
const firstExportEnd = firstExport.end;
const afterFirstExport = source.slice(firstExportEnd, firstExportEnd + 9);
isTranspilerExport = afterFirstExport === ' exports.' || afterFirstExport === ' void 0;\n';
}
if (isTranspilerExport === false) {
return addError(importsExports, `Duplicate exported name \`${name}\` in \`${token}.... = ...\` statement`, start, end);
}
}
const position = getPosition(importsExports, start, end);
commonJsExports[name] = startsWithModule ? { ...position, startsWithModule } : position;
return;
}
if (startsWithModule === false) {
return addError(importsExports, `\`${token} = ...\` is not valid CommonJS namespace export (use \`module.exports = ...\` instead)`, start, end);
}
if (importsExports.commonJsNamespaceExport !== undefined) {
return addError(importsExports, `Duplicate CommonJS namespace export (\`${token} = ...\`)`, start, end);
}
importsExports.commonJsNamespaceExport = getPosition(importsExports, start, end);
};

View file

@ -0,0 +1,47 @@
import { parseIdentifier } from './partParsers.js';
import { addError, getPosition, stripComments } from './utils.js';
/**
* Adds error of parsing `module.exports = ...`/`(module.)exports.foo = ...` statement.
*/
export const onCommonJsExportError = (importsExports, source, { start, end, token }) => addError(importsExports, `Cannot find end (equal sign) of \`${token[0] === 'm' ? 'module.' : ''}exports${source[end] === '.' ? '.' : ''}... = ...\` statement`, start, end);
/**
* Parses `module.exports = ...`/`(module.)exports.foo = ...` statement.
*/
export const onCommonJsExportParse = (importsExports, source, { start, end: unparsedStart, comments, token }, { start: unparsedEnd, end }) => {
var unparsed = stripComments(source, unparsedStart, unparsedEnd, comments).trim();
const startsWithModule = token[0] === 'm';
if (unparsed[0] === '.') {
unparsed = unparsed.slice(1).trim();
const nameIndex = parseIdentifier(unparsed);
if (nameIndex === 0) {
return addError(importsExports, `Cannot parse identifier of \`${token}.... = ...\` statement`, start, end);
}
const name = unparsed.slice(0, nameIndex);
let { commonJsExports } = importsExports;
if (commonJsExports === undefined) {
importsExports.commonJsExports = commonJsExports = { __proto__: null };
}
else if (name in commonJsExports) {
const firstExport = commonJsExports[name];
let isTranspilerExport = false;
if (startsWithModule === false && firstExport.startsWithModule === undefined) {
const firstExportEnd = firstExport.end;
const afterFirstExport = source.slice(firstExportEnd, firstExportEnd + 9);
isTranspilerExport = afterFirstExport === ' exports.' || afterFirstExport === ' void 0;\n';
}
if (isTranspilerExport === false) {
return addError(importsExports, `Duplicate exported name \`${name}\` in \`${token}.... = ...\` statement`, start, end);
}
}
const position = getPosition(importsExports, start, end);
commonJsExports[name] = startsWithModule ? { ...position, startsWithModule } : position;
return;
}
if (startsWithModule === false) {
return addError(importsExports, `\`${token} = ...\` is not valid CommonJS namespace export (use \`module.exports = ...\` instead)`, start, end);
}
if (importsExports.commonJsNamespaceExport !== undefined) {
return addError(importsExports, `Duplicate CommonJS namespace export (\`${token} = ...\`)`, start, end);
}
importsExports.commonJsNamespaceExport = getPosition(importsExports, start, end);
};

View file

@ -0,0 +1,282 @@
'use strict';
exports.onDeclarationExportError = undefined;
exports.onDeclarationExportParse = undefined;
const { parseDestructuring, parseFrom, parseIdentifier, parseWith } = require('./partParsers.cjs');
const { addError, getPosition, stripComments } = require('./utils.cjs');
/**
* Adds error of parsing `export` statement with declaration.
*/
const onDeclarationExportError = exports.onDeclarationExportError = (importsExports, _source, { start, end }) => addError(importsExports, 'Cannot find end of export with declaration', start, end);
/**
* Parses `export` statement with declaration.
*/
const onDeclarationExportParse = exports.onDeclarationExportParse = (importsExports, source, { start, end: unparsedStart, comments }, { start: unparsedEnd, end: exportEnd, match: endMatch }) => {
var end = exportEnd;
var isDeclare = false;
var isType = false;
var unparsed = stripComments(source, unparsedStart, unparsedEnd, comments).trim();
if (unparsed.startsWith('declare ')) {
isDeclare = true;
unparsed = unparsed.slice(8).trim();
}
if (unparsed.startsWith('type ')) {
isType = true;
unparsed = unparsed.slice(5).trim();
}
const modifiers = `${isDeclare ? 'declare ' : ''}${isType ? 'type ' : ''}`;
if (unparsed[0] === '*') {
if (isDeclare) {
return addError(importsExports, `Cannot declare star export (\`export ${modifiers}* ... from ...\`)`, start, end);
}
let namespace;
if (unparsed.startsWith('* as ')) {
unparsed = unparsed.slice(5).trim();
const spaceIndex = unparsed.indexOf(' ');
if (spaceIndex === -1) {
return addError(importsExports, `Cannot find namespace of \`export ${modifiers}* as ... from ...\` statement`, start, end);
}
namespace = unparsed.slice(0, spaceIndex);
unparsed = unparsed.slice(spaceIndex + 1);
}
if (unparsed[unparsed.length - 1] === ';') {
unparsed = unparsed.slice(0, -1).trim();
}
const { groups } = endMatch;
let quoteCharacter;
if (groups.with === undefined) {
quoteCharacter = unparsed[unparsed.length - 1];
unparsed = unparsed.slice(0, -1);
}
else {
quoteCharacter = groups.with[0];
}
const reexportKind = `${namespace === undefined ? 'star' : 'namespace'} reexport`;
if (quoteCharacter !== "'" && quoteCharacter !== '"') {
return addError(importsExports, `Cannot find end of \`from\` string literal of ${reexportKind}`, start, end);
}
const { from, index } = parseFrom(quoteCharacter, unparsed);
if (index === -1) {
return addError(importsExports, `Cannot find start of \`from\` string literal of ${reexportKind}`, start, end);
}
let withAttributes;
if (groups.with !== undefined) {
if (isType) {
return addError(importsExports, `Cannot use import attributes (\`with {...}\`) in \`export ${modifiers}\` statement for ${reexportKind} from \`${from}\``, start, end);
}
const attributes = parseWith(exportEnd, source);
if (attributes === undefined) {
return addError(importsExports, `Cannot find end of import attributes (\`with {...}\`) for ${reexportKind} from \`${from}\``, start, end);
}
end = attributes.endIndex;
withAttributes = attributes.with;
if (withAttributes === undefined) {
return addError(importsExports, `Cannot parse import attributes (\`with {...}\`) for ${reexportKind} from \`${from}\``, start, end);
}
}
const position = getPosition(importsExports, start, end);
const parsedReexport = withAttributes === undefined ? position : { ...position, with: withAttributes };
let key = 'starReexports';
if (namespace !== undefined) {
parsedReexport.namespace = namespace;
key = 'namespaceReexports';
}
if (isType) {
key = key === 'starReexports' ? 'typeStarReexports' : 'typeNamespaceReexports';
}
let reexports = importsExports[key];
reexports !== null && reexports !== void 0 ? reexports : (reexports = importsExports[key] = { __proto__: null });
let reexportsList = reexports[from];
if (reexportsList === undefined) {
reexports[from] = [parsedReexport];
}
else {
reexportsList.push(parsedReexport);
}
return end;
}
const identifierIndex = parseIdentifier(unparsed);
if (identifierIndex === 0) {
return addError(importsExports, `Cannot parse declaration identifier of \`export ${modifiers}...\` statement`, start, end);
}
const identifier = unparsed.slice(0, identifierIndex);
if (isType) {
let { typeExports } = importsExports;
if (typeExports === undefined) {
importsExports.typeExports = typeExports = { __proto__: null };
}
else if (identifier in typeExports) {
return addError(importsExports, `Duplicate exported type \`${identifier}\``, start, end);
}
typeExports[identifier] = getPosition(importsExports, start, end);
if (isDeclare) {
typeExports[identifier].isDeclare = true;
}
return;
}
if (identifier === 'default') {
if (isDeclare) {
return addError(importsExports, `Cannot export default with declare (\`export ${modifiers}default ...\`)`, start, end);
}
if (importsExports.defaultExport !== undefined) {
return addError(importsExports, 'Duplicate default export', start, end);
}
importsExports.defaultExport = getPosition(importsExports, start, end);
return;
}
unparsed = unparsed.slice(identifierIndex).trim();
if (identifier === 'interface') {
const nameIndex = parseIdentifier(unparsed);
if (nameIndex === 0) {
return addError(importsExports, `Cannot parse interface identifier of \`export ${modifiers}interface ...\` statement`, start, end);
}
const name = unparsed.slice(0, nameIndex);
let { interfaceExports } = importsExports;
interfaceExports !== null && interfaceExports !== void 0 ? interfaceExports : (interfaceExports = importsExports.interfaceExports = { __proto__: null });
const interfaceExport = getPosition(importsExports, start, end);
if (isDeclare) {
interfaceExport.isDeclare = true;
}
let exportsList = interfaceExports[name];
if (exportsList === undefined) {
interfaceExports[name] = [interfaceExport];
}
else {
exportsList.push(interfaceExport);
}
return;
}
if (identifier === 'namespace') {
const nameIndex = parseIdentifier(unparsed);
if (nameIndex === 0) {
return addError(importsExports, `Cannot parse namespace identifier of \`export ${modifiers}namespace ...\` statement`, start, end);
}
const name = unparsed.slice(0, nameIndex);
let { namespaceExports } = importsExports;
namespaceExports !== null && namespaceExports !== void 0 ? namespaceExports : (namespaceExports = importsExports.namespaceExports = { __proto__: null });
const namespaceExport = getPosition(importsExports, start, end);
if (isDeclare) {
namespaceExport.isDeclare = true;
}
let exportsList = namespaceExports[name];
if (exportsList === undefined) {
namespaceExports[name] = [namespaceExport];
}
else {
exportsList.push(namespaceExport);
}
return;
}
var isAsync = false;
var kind;
const names = [];
switch (identifier) {
case 'const':
case 'class':
case 'enum':
case 'let':
case 'var':
if ((identifier === 'const' || identifier === 'let' || identifier === 'var') &&
(unparsed[0] === '{' || unparsed[0] === '[')) {
const destructuring = parseDestructuring(unparsed + source.slice(end));
if (destructuring === undefined) {
return addError(importsExports, `Cannot parse destructuring names in \`export ${modifiers}${identifier} ...\` statement`, start, end);
}
const endDiff = destructuring.endIndex - unparsed.length;
if (endDiff > 0) {
end += endDiff;
}
names.push(...destructuring.names);
kind = `destructuring ${identifier}`;
if (isDeclare) {
kind = `declare ${kind}`;
}
break;
}
const nameIndex = parseIdentifier(unparsed);
if (nameIndex === 0) {
return addError(importsExports, `Cannot parse \`${identifier}\` identifier of \`export ${modifiers}${identifier} ...\` statement`, start, end);
}
kind = identifier;
if (isDeclare) {
kind = `declare ${kind}`;
}
names[0] = unparsed.slice(0, nameIndex);
if (identifier === 'const' && names[0] === 'enum') {
unparsed = unparsed.slice(4).trim();
const constEnumNameIndex = parseIdentifier(unparsed);
if (constEnumNameIndex === 0) {
return addError(importsExports, `Cannot parse identifier of \`export ${modifiers}const enum ...\` statement`, start, end);
}
kind = 'const enum';
if (isDeclare) {
kind = `declare ${kind}`;
}
names[0] = unparsed.slice(0, constEnumNameIndex);
}
break;
case 'abstract':
if (!unparsed.startsWith('class ')) {
return addError(importsExports, `Cannot parse declaration of abstract class of \`export ${modifiers}abstract ...\` statement`, start, end);
}
unparsed = unparsed.slice(5).trim();
const abstractClassNameIndex = parseIdentifier(unparsed);
if (abstractClassNameIndex === 0) {
return addError(importsExports, `Cannot parse \`${identifier}\` identifier of \`export ${modifiers}abstract class ${identifier} ...\` statement`, start, end);
}
kind = 'abstract class';
if (isDeclare) {
kind = `declare ${kind}`;
}
names[0] = unparsed.slice(0, abstractClassNameIndex);
break;
// @ts-expect-error
case 'async':
if (isDeclare) {
return addError(importsExports, `Cannot export async function with declare (\`export ${modifiers}async ...\`)`, start, end);
}
if (unparsed.startsWith('function') === false) {
return addError(importsExports, 'Cannot parse async function in `export async ...` statement', start, end);
}
isAsync = true;
unparsed = unparsed.slice(8).trim();
case 'function':
if (unparsed[0] === '*') {
if (isDeclare) {
return addError(importsExports, `Cannot export generator function with declare (\`export ${modifiers}function* ...\`)`, start, end);
}
unparsed = unparsed.slice(1).trim();
kind = 'function*';
}
else {
kind = 'function';
}
if (isAsync) {
kind = `async ${kind}`;
}
else if (isDeclare) {
kind = 'declare function';
}
const functionNameIndex = parseIdentifier(unparsed);
if (functionNameIndex === 0) {
return addError(importsExports, `Cannot parse \`${kind}\` identifier of \`export ${modifiers}${kind} ...\` statement`, start, end);
}
names[0] = unparsed.slice(0, functionNameIndex);
break;
default:
return addError(importsExports, `Cannot parse \`export ${modifiers}${identifier} ...\` statement`, start, end);
}
var { declarationExports } = importsExports;
declarationExports !== null && declarationExports !== void 0 ? declarationExports : (declarationExports = importsExports.declarationExports = {
__proto__: null,
});
for (const name of names) {
if (name in declarationExports) {
return addError(importsExports, `Duplicate exported declaration \`${kind} ${name}\``, start, end);
}
declarationExports[name] = {
...getPosition(importsExports, start, end),
kind: kind,
};
}
return end;
};

View file

@ -0,0 +1,279 @@
import { parseDestructuring, parseFrom, parseIdentifier, parseWith } from './partParsers.js';
import { addError, getPosition, stripComments } from './utils.js';
/**
* Adds error of parsing `export` statement with declaration.
*/
export const onDeclarationExportError = (importsExports, _source, { start, end }) => addError(importsExports, 'Cannot find end of export with declaration', start, end);
/**
* Parses `export` statement with declaration.
*/
export const onDeclarationExportParse = (importsExports, source, { start, end: unparsedStart, comments }, { start: unparsedEnd, end: exportEnd, match: endMatch }) => {
var end = exportEnd;
var isDeclare = false;
var isType = false;
var unparsed = stripComments(source, unparsedStart, unparsedEnd, comments).trim();
if (unparsed.startsWith('declare ')) {
isDeclare = true;
unparsed = unparsed.slice(8).trim();
}
if (unparsed.startsWith('type ')) {
isType = true;
unparsed = unparsed.slice(5).trim();
}
const modifiers = `${isDeclare ? 'declare ' : ''}${isType ? 'type ' : ''}`;
if (unparsed[0] === '*') {
if (isDeclare) {
return addError(importsExports, `Cannot declare star export (\`export ${modifiers}* ... from ...\`)`, start, end);
}
let namespace;
if (unparsed.startsWith('* as ')) {
unparsed = unparsed.slice(5).trim();
const spaceIndex = unparsed.indexOf(' ');
if (spaceIndex === -1) {
return addError(importsExports, `Cannot find namespace of \`export ${modifiers}* as ... from ...\` statement`, start, end);
}
namespace = unparsed.slice(0, spaceIndex);
unparsed = unparsed.slice(spaceIndex + 1);
}
if (unparsed[unparsed.length - 1] === ';') {
unparsed = unparsed.slice(0, -1).trim();
}
const { groups } = endMatch;
let quoteCharacter;
if (groups.with === undefined) {
quoteCharacter = unparsed[unparsed.length - 1];
unparsed = unparsed.slice(0, -1);
}
else {
quoteCharacter = groups.with[0];
}
const reexportKind = `${namespace === undefined ? 'star' : 'namespace'} reexport`;
if (quoteCharacter !== "'" && quoteCharacter !== '"') {
return addError(importsExports, `Cannot find end of \`from\` string literal of ${reexportKind}`, start, end);
}
const { from, index } = parseFrom(quoteCharacter, unparsed);
if (index === -1) {
return addError(importsExports, `Cannot find start of \`from\` string literal of ${reexportKind}`, start, end);
}
let withAttributes;
if (groups.with !== undefined) {
if (isType) {
return addError(importsExports, `Cannot use import attributes (\`with {...}\`) in \`export ${modifiers}\` statement for ${reexportKind} from \`${from}\``, start, end);
}
const attributes = parseWith(exportEnd, source);
if (attributes === undefined) {
return addError(importsExports, `Cannot find end of import attributes (\`with {...}\`) for ${reexportKind} from \`${from}\``, start, end);
}
end = attributes.endIndex;
withAttributes = attributes.with;
if (withAttributes === undefined) {
return addError(importsExports, `Cannot parse import attributes (\`with {...}\`) for ${reexportKind} from \`${from}\``, start, end);
}
}
const position = getPosition(importsExports, start, end);
const parsedReexport = withAttributes === undefined ? position : { ...position, with: withAttributes };
let key = 'starReexports';
if (namespace !== undefined) {
parsedReexport.namespace = namespace;
key = 'namespaceReexports';
}
if (isType) {
key = key === 'starReexports' ? 'typeStarReexports' : 'typeNamespaceReexports';
}
let reexports = importsExports[key];
reexports !== null && reexports !== void 0 ? reexports : (reexports = importsExports[key] = { __proto__: null });
let reexportsList = reexports[from];
if (reexportsList === undefined) {
reexports[from] = [parsedReexport];
}
else {
reexportsList.push(parsedReexport);
}
return end;
}
const identifierIndex = parseIdentifier(unparsed);
if (identifierIndex === 0) {
return addError(importsExports, `Cannot parse declaration identifier of \`export ${modifiers}...\` statement`, start, end);
}
const identifier = unparsed.slice(0, identifierIndex);
if (isType) {
let { typeExports } = importsExports;
if (typeExports === undefined) {
importsExports.typeExports = typeExports = { __proto__: null };
}
else if (identifier in typeExports) {
return addError(importsExports, `Duplicate exported type \`${identifier}\``, start, end);
}
typeExports[identifier] = getPosition(importsExports, start, end);
if (isDeclare) {
typeExports[identifier].isDeclare = true;
}
return;
}
if (identifier === 'default') {
if (isDeclare) {
return addError(importsExports, `Cannot export default with declare (\`export ${modifiers}default ...\`)`, start, end);
}
if (importsExports.defaultExport !== undefined) {
return addError(importsExports, 'Duplicate default export', start, end);
}
importsExports.defaultExport = getPosition(importsExports, start, end);
return;
}
unparsed = unparsed.slice(identifierIndex).trim();
if (identifier === 'interface') {
const nameIndex = parseIdentifier(unparsed);
if (nameIndex === 0) {
return addError(importsExports, `Cannot parse interface identifier of \`export ${modifiers}interface ...\` statement`, start, end);
}
const name = unparsed.slice(0, nameIndex);
let { interfaceExports } = importsExports;
interfaceExports !== null && interfaceExports !== void 0 ? interfaceExports : (interfaceExports = importsExports.interfaceExports = { __proto__: null });
const interfaceExport = getPosition(importsExports, start, end);
if (isDeclare) {
interfaceExport.isDeclare = true;
}
let exportsList = interfaceExports[name];
if (exportsList === undefined) {
interfaceExports[name] = [interfaceExport];
}
else {
exportsList.push(interfaceExport);
}
return;
}
if (identifier === 'namespace') {
const nameIndex = parseIdentifier(unparsed);
if (nameIndex === 0) {
return addError(importsExports, `Cannot parse namespace identifier of \`export ${modifiers}namespace ...\` statement`, start, end);
}
const name = unparsed.slice(0, nameIndex);
let { namespaceExports } = importsExports;
namespaceExports !== null && namespaceExports !== void 0 ? namespaceExports : (namespaceExports = importsExports.namespaceExports = { __proto__: null });
const namespaceExport = getPosition(importsExports, start, end);
if (isDeclare) {
namespaceExport.isDeclare = true;
}
let exportsList = namespaceExports[name];
if (exportsList === undefined) {
namespaceExports[name] = [namespaceExport];
}
else {
exportsList.push(namespaceExport);
}
return;
}
var isAsync = false;
var kind;
const names = [];
switch (identifier) {
case 'const':
case 'class':
case 'enum':
case 'let':
case 'var':
if ((identifier === 'const' || identifier === 'let' || identifier === 'var') &&
(unparsed[0] === '{' || unparsed[0] === '[')) {
const destructuring = parseDestructuring(unparsed + source.slice(end));
if (destructuring === undefined) {
return addError(importsExports, `Cannot parse destructuring names in \`export ${modifiers}${identifier} ...\` statement`, start, end);
}
const endDiff = destructuring.endIndex - unparsed.length;
if (endDiff > 0) {
end += endDiff;
}
names.push(...destructuring.names);
kind = `destructuring ${identifier}`;
if (isDeclare) {
kind = `declare ${kind}`;
}
break;
}
const nameIndex = parseIdentifier(unparsed);
if (nameIndex === 0) {
return addError(importsExports, `Cannot parse \`${identifier}\` identifier of \`export ${modifiers}${identifier} ...\` statement`, start, end);
}
kind = identifier;
if (isDeclare) {
kind = `declare ${kind}`;
}
names[0] = unparsed.slice(0, nameIndex);
if (identifier === 'const' && names[0] === 'enum') {
unparsed = unparsed.slice(4).trim();
const constEnumNameIndex = parseIdentifier(unparsed);
if (constEnumNameIndex === 0) {
return addError(importsExports, `Cannot parse identifier of \`export ${modifiers}const enum ...\` statement`, start, end);
}
kind = 'const enum';
if (isDeclare) {
kind = `declare ${kind}`;
}
names[0] = unparsed.slice(0, constEnumNameIndex);
}
break;
case 'abstract':
if (!unparsed.startsWith('class ')) {
return addError(importsExports, `Cannot parse declaration of abstract class of \`export ${modifiers}abstract ...\` statement`, start, end);
}
unparsed = unparsed.slice(5).trim();
const abstractClassNameIndex = parseIdentifier(unparsed);
if (abstractClassNameIndex === 0) {
return addError(importsExports, `Cannot parse \`${identifier}\` identifier of \`export ${modifiers}abstract class ${identifier} ...\` statement`, start, end);
}
kind = 'abstract class';
if (isDeclare) {
kind = `declare ${kind}`;
}
names[0] = unparsed.slice(0, abstractClassNameIndex);
break;
// @ts-expect-error
case 'async':
if (isDeclare) {
return addError(importsExports, `Cannot export async function with declare (\`export ${modifiers}async ...\`)`, start, end);
}
if (unparsed.startsWith('function') === false) {
return addError(importsExports, 'Cannot parse async function in `export async ...` statement', start, end);
}
isAsync = true;
unparsed = unparsed.slice(8).trim();
case 'function':
if (unparsed[0] === '*') {
if (isDeclare) {
return addError(importsExports, `Cannot export generator function with declare (\`export ${modifiers}function* ...\`)`, start, end);
}
unparsed = unparsed.slice(1).trim();
kind = 'function*';
}
else {
kind = 'function';
}
if (isAsync) {
kind = `async ${kind}`;
}
else if (isDeclare) {
kind = 'declare function';
}
const functionNameIndex = parseIdentifier(unparsed);
if (functionNameIndex === 0) {
return addError(importsExports, `Cannot parse \`${kind}\` identifier of \`export ${modifiers}${kind} ...\` statement`, start, end);
}
names[0] = unparsed.slice(0, functionNameIndex);
break;
default:
return addError(importsExports, `Cannot parse \`export ${modifiers}${identifier} ...\` statement`, start, end);
}
var { declarationExports } = importsExports;
declarationExports !== null && declarationExports !== void 0 ? declarationExports : (declarationExports = importsExports.declarationExports = {
__proto__: null,
});
for (const name of names) {
if (name in declarationExports) {
return addError(importsExports, `Duplicate exported declaration \`${kind} ${name}\``, start, end);
}
declarationExports[name] = {
...getPosition(importsExports, start, end),
kind: kind,
};
}
return end;
};

View file

@ -0,0 +1,56 @@
'use strict';
exports.onDynamicImportError = undefined;
exports.onDynamicImportParse = undefined;
const { parseFrom, parseWith } = require('./partParsers.cjs');
const { addError, getPosition } = require('./utils.cjs');
/**
* Adds error of parsing `import('...')`/`import("...")` statement.
*/
const onDynamicImportError = exports.onDynamicImportError = (importsExports, _source, { start, end }) => addError(importsExports, 'Cannot find end of `import(...)` statement', start, end);
/**
* Parses `import('...')`/`import("...")` statement.
*/
const onDynamicImportParse = exports.onDynamicImportParse = (importsExports, source, { start }, { start: unparsedStart }, { start: unparsedEnd, end: importEnd, token: endToken }) => {
var end = importEnd;
const unparsed = source.slice(unparsedStart, unparsedEnd);
const isType = source.slice(start - 7, start) === 'typeof ';
const quoteCharacter = endToken[0];
if (quoteCharacter === undefined) {
return addError(importsExports, `Cannot find end of path string literal of dynamic \`import(...)\`${isType ? ' of type' : ''}`, start, end);
}
const { from, index } = parseFrom(quoteCharacter, unparsed);
const importKind = `dynamic \`import(${quoteCharacter}...${quoteCharacter})\`${isType ? ' of type' : ''} from \`${from}\``;
if (index !== 0) {
return addError(importsExports, `Cannot find start of path string literal of ${importKind}`, start, end);
}
var withAttributes;
if (source[end] === ',') {
const withIndex = source.indexOf('with: {', end + 1);
if (withIndex !== -1 && source.slice(end + 1, withIndex).trim() === '{') {
const attributes = parseWith(withIndex + 7, source);
if (attributes === undefined) {
return addError(importsExports, `Cannot find end of import attributes (\`with: {...}\`) in ${importKind}`, start, end);
}
end = attributes.endIndex;
withAttributes = attributes.with;
if (withAttributes === undefined) {
return addError(importsExports, `Cannot parse import attributes (\`with: {...}\`) for ${importKind}`, start, end);
}
}
}
const position = getPosition(importsExports, start, end);
const parsedImport = withAttributes === undefined ? position : { ...position, with: withAttributes };
var key = 'dynamicImports';
if (isType) {
key = 'typeDynamicImports';
}
var imports = importsExports[key];
imports !== null && imports !== void 0 ? imports : (imports = importsExports[key] = { __proto__: null });
var importsList = imports[from];
if (importsList === undefined) {
imports[from] = [parsedImport];
}
else {
importsList.push(parsedImport);
}
};

View file

@ -0,0 +1,53 @@
import { parseFrom, parseWith } from './partParsers.js';
import { addError, getPosition } from './utils.js';
/**
* Adds error of parsing `import('...')`/`import("...")` statement.
*/
export const onDynamicImportError = (importsExports, _source, { start, end }) => addError(importsExports, 'Cannot find end of `import(...)` statement', start, end);
/**
* Parses `import('...')`/`import("...")` statement.
*/
export const onDynamicImportParse = (importsExports, source, { start }, { start: unparsedStart }, { start: unparsedEnd, end: importEnd, token: endToken }) => {
var end = importEnd;
const unparsed = source.slice(unparsedStart, unparsedEnd);
const isType = source.slice(start - 7, start) === 'typeof ';
const quoteCharacter = endToken[0];
if (quoteCharacter === undefined) {
return addError(importsExports, `Cannot find end of path string literal of dynamic \`import(...)\`${isType ? ' of type' : ''}`, start, end);
}
const { from, index } = parseFrom(quoteCharacter, unparsed);
const importKind = `dynamic \`import(${quoteCharacter}...${quoteCharacter})\`${isType ? ' of type' : ''} from \`${from}\``;
if (index !== 0) {
return addError(importsExports, `Cannot find start of path string literal of ${importKind}`, start, end);
}
var withAttributes;
if (source[end] === ',') {
const withIndex = source.indexOf('with: {', end + 1);
if (withIndex !== -1 && source.slice(end + 1, withIndex).trim() === '{') {
const attributes = parseWith(withIndex + 7, source);
if (attributes === undefined) {
return addError(importsExports, `Cannot find end of import attributes (\`with: {...}\`) in ${importKind}`, start, end);
}
end = attributes.endIndex;
withAttributes = attributes.with;
if (withAttributes === undefined) {
return addError(importsExports, `Cannot parse import attributes (\`with: {...}\`) for ${importKind}`, start, end);
}
}
}
const position = getPosition(importsExports, start, end);
const parsedImport = withAttributes === undefined ? position : { ...position, with: withAttributes };
var key = 'dynamicImports';
if (isType) {
key = 'typeDynamicImports';
}
var imports = importsExports[key];
imports !== null && imports !== void 0 ? imports : (imports = importsExports[key] = { __proto__: null });
var importsList = imports[from];
if (importsList === undefined) {
imports[from] = [parsedImport];
}
else {
importsList.push(parsedImport);
}
};

View file

@ -0,0 +1,139 @@
'use strict';
exports.onImportError = undefined;
exports.onImportParse = undefined;
const { parseFrom, parseWith } = require('./partParsers.cjs');
const { addError, getPosition, spacesRegExp, stripComments } = require('./utils.cjs');
/**
* Adds error of parsing `import` statement.
*/
const onImportError = exports.onImportError = (importsExports, _source, { start, end }) => addError(importsExports, 'Cannot find end of `import` statement', start, end);
/**
* Parses `import` statement.
*/
const onImportParse = exports.onImportParse = (importsExports, source, { start, end: unparsedStart, comments }, { start: unparsedEnd, end: importEnd, match: endMatch, token: endToken }) => {
var end = importEnd;
var unparsed = stripComments(source, unparsedStart, unparsedEnd, comments);
const quoteCharacter = endToken[0];
const { from, index } = parseFrom(quoteCharacter, unparsed);
if (index === -1) {
return addError(importsExports, 'Cannot find start of `from` string literal of import', start, end);
}
unparsed = unparsed.slice(0, index).trim().replace(spacesRegExp, ' ');
var isType = false;
if (unparsed.startsWith('type ')) {
isType = true;
unparsed = unparsed.slice(5);
}
if (unparsed.endsWith(' from')) {
unparsed = unparsed.slice(0, -5);
}
var withAttributes;
if (endMatch.groups['with'] !== undefined) {
if (isType) {
return addError(importsExports, `Cannot use import attributes (\`with {...}\`) in \`import type\` statement for import from \`${from}\``, start, end);
}
const attributes = parseWith(importEnd, source);
if (attributes === undefined) {
return addError(importsExports, `Cannot find end of import attributes (\`with {...}\`) in \`import\` statement for import from \`${from}\``, start, end);
}
end = attributes.endIndex;
withAttributes = attributes.with;
if (withAttributes === undefined) {
return addError(importsExports, `Cannot parse import attributes (\`with {...}\`) in \`import\` statement for import from \`${from}\``, start, end);
}
}
const position = getPosition(importsExports, start, end);
const parsedImport = withAttributes === undefined ? position : { ...position, with: withAttributes };
const namespaceIndex = unparsed.indexOf('* as ');
var key = 'namedImports';
if (namespaceIndex === -1) {
const braceIndex = unparsed.indexOf('{');
if (braceIndex !== -1) {
let namesString = unparsed.slice(braceIndex + 1);
const braceCloseIndex = namesString.lastIndexOf('}');
unparsed = unparsed.slice(0, braceIndex);
if (braceCloseIndex === -1) {
return addError(importsExports, `Cannot find end of imports list (\`}\`) for import from \`${from}\``, start, end);
}
namesString = namesString.slice(0, braceCloseIndex).trim();
const namesList = namesString.split(',');
let names;
let types;
for (let name of namesList) {
let isTypeName = false;
name = name.trim();
if (name === '') {
continue;
}
const nameObject = {};
if (name.startsWith('type ')) {
if (isType) {
return addError(importsExports, `Cannot use \`type\` modifier in \`import type\` statement for type \`${name.slice(5)}\` for import from \`${from}\``, start, end);
}
isTypeName = true;
name = name.slice(5);
}
const asIndex = name.indexOf(' as ');
if (asIndex !== -1) {
nameObject.by = name.slice(0, asIndex);
name = name.slice(asIndex + 4);
}
if (isTypeName) {
if (types === undefined) {
types = { __proto__: null };
}
else if (name in types) {
return addError(importsExports, `Duplicate imported type \`${name}\` for import from \`${from}\``, start, end);
}
types[name] = nameObject;
}
else {
if (names === undefined) {
names = { __proto__: null };
}
else if (name in names) {
return addError(importsExports, `Duplicate imported name \`${name}\` for import from \`${from}\``, start, end);
}
names[name] = nameObject;
}
}
if (names !== undefined) {
parsedImport.names = names;
}
if (types !== undefined) {
parsedImport.types = types;
}
}
}
else {
parsedImport.namespace = unparsed.slice(namespaceIndex + 5);
key = 'namespaceImports';
unparsed = unparsed.slice(0, namespaceIndex);
}
const commaIndex = unparsed.indexOf(',');
if (commaIndex !== -1) {
unparsed = unparsed.slice(0, commaIndex).trim();
}
else {
unparsed = unparsed.trim();
}
if (unparsed !== '') {
if (isType && key === 'namespaceImports') {
return addError(importsExports, `Cannot use default \`${unparsed}\` and namespace \`${parsedImport.namespace}\` together in \`import type\` statement for import from \`${from}\``, start, end);
}
parsedImport.default = unparsed;
}
if (isType) {
key = key === 'namedImports' ? 'typeNamedImports' : 'typeNamespaceImports';
}
var imports = importsExports[key];
imports !== null && imports !== void 0 ? imports : (imports = importsExports[key] = { __proto__: null });
var importsList = imports[from];
if (importsList === undefined) {
imports[from] = [parsedImport];
}
else {
importsList.push(parsedImport);
}
return end;
};

View file

@ -0,0 +1,136 @@
import { parseFrom, parseWith } from './partParsers.js';
import { addError, getPosition, spacesRegExp, stripComments } from './utils.js';
/**
* Adds error of parsing `import` statement.
*/
export const onImportError = (importsExports, _source, { start, end }) => addError(importsExports, 'Cannot find end of `import` statement', start, end);
/**
* Parses `import` statement.
*/
export const onImportParse = (importsExports, source, { start, end: unparsedStart, comments }, { start: unparsedEnd, end: importEnd, match: endMatch, token: endToken }) => {
var end = importEnd;
var unparsed = stripComments(source, unparsedStart, unparsedEnd, comments);
const quoteCharacter = endToken[0];
const { from, index } = parseFrom(quoteCharacter, unparsed);
if (index === -1) {
return addError(importsExports, 'Cannot find start of `from` string literal of import', start, end);
}
unparsed = unparsed.slice(0, index).trim().replace(spacesRegExp, ' ');
var isType = false;
if (unparsed.startsWith('type ')) {
isType = true;
unparsed = unparsed.slice(5);
}
if (unparsed.endsWith(' from')) {
unparsed = unparsed.slice(0, -5);
}
var withAttributes;
if (endMatch.groups['with'] !== undefined) {
if (isType) {
return addError(importsExports, `Cannot use import attributes (\`with {...}\`) in \`import type\` statement for import from \`${from}\``, start, end);
}
const attributes = parseWith(importEnd, source);
if (attributes === undefined) {
return addError(importsExports, `Cannot find end of import attributes (\`with {...}\`) in \`import\` statement for import from \`${from}\``, start, end);
}
end = attributes.endIndex;
withAttributes = attributes.with;
if (withAttributes === undefined) {
return addError(importsExports, `Cannot parse import attributes (\`with {...}\`) in \`import\` statement for import from \`${from}\``, start, end);
}
}
const position = getPosition(importsExports, start, end);
const parsedImport = withAttributes === undefined ? position : { ...position, with: withAttributes };
const namespaceIndex = unparsed.indexOf('* as ');
var key = 'namedImports';
if (namespaceIndex === -1) {
const braceIndex = unparsed.indexOf('{');
if (braceIndex !== -1) {
let namesString = unparsed.slice(braceIndex + 1);
const braceCloseIndex = namesString.lastIndexOf('}');
unparsed = unparsed.slice(0, braceIndex);
if (braceCloseIndex === -1) {
return addError(importsExports, `Cannot find end of imports list (\`}\`) for import from \`${from}\``, start, end);
}
namesString = namesString.slice(0, braceCloseIndex).trim();
const namesList = namesString.split(',');
let names;
let types;
for (let name of namesList) {
let isTypeName = false;
name = name.trim();
if (name === '') {
continue;
}
const nameObject = {};
if (name.startsWith('type ')) {
if (isType) {
return addError(importsExports, `Cannot use \`type\` modifier in \`import type\` statement for type \`${name.slice(5)}\` for import from \`${from}\``, start, end);
}
isTypeName = true;
name = name.slice(5);
}
const asIndex = name.indexOf(' as ');
if (asIndex !== -1) {
nameObject.by = name.slice(0, asIndex);
name = name.slice(asIndex + 4);
}
if (isTypeName) {
if (types === undefined) {
types = { __proto__: null };
}
else if (name in types) {
return addError(importsExports, `Duplicate imported type \`${name}\` for import from \`${from}\``, start, end);
}
types[name] = nameObject;
}
else {
if (names === undefined) {
names = { __proto__: null };
}
else if (name in names) {
return addError(importsExports, `Duplicate imported name \`${name}\` for import from \`${from}\``, start, end);
}
names[name] = nameObject;
}
}
if (names !== undefined) {
parsedImport.names = names;
}
if (types !== undefined) {
parsedImport.types = types;
}
}
}
else {
parsedImport.namespace = unparsed.slice(namespaceIndex + 5);
key = 'namespaceImports';
unparsed = unparsed.slice(0, namespaceIndex);
}
const commaIndex = unparsed.indexOf(',');
if (commaIndex !== -1) {
unparsed = unparsed.slice(0, commaIndex).trim();
}
else {
unparsed = unparsed.trim();
}
if (unparsed !== '') {
if (isType && key === 'namespaceImports') {
return addError(importsExports, `Cannot use default \`${unparsed}\` and namespace \`${parsedImport.namespace}\` together in \`import type\` statement for import from \`${from}\``, start, end);
}
parsedImport.default = unparsed;
}
if (isType) {
key = key === 'namedImports' ? 'typeNamedImports' : 'typeNamespaceImports';
}
var imports = importsExports[key];
imports !== null && imports !== void 0 ? imports : (imports = importsExports[key] = { __proto__: null });
var importsList = imports[from];
if (importsList === undefined) {
imports[from] = [parsedImport];
}
else {
importsList.push(parsedImport);
}
return end;
};

View file

@ -0,0 +1,55 @@
'use strict';
exports.parseImportsExports = undefined;
const { onGlobalError } = require('./onErrors.cjs');
const { comments, getStatements } = require('./statements.cjs');
const { CONTEXT_KEY, createParseFunction, getCacheKey, removeErrorsCausedByOverloading, } = require('./utils.cjs');
/**
* Parses `import`/`export` in ECMAScript/TypeScript syntax.
*/
const parseImportsExports = exports.parseImportsExports = (source, options) => {
const cacheKey = getCacheKey(options);
const importsExports = {
namedImports: undefined,
namespaceImports: undefined,
dynamicImports: undefined,
requires: undefined,
typeNamedImports: undefined,
typeNamespaceImports: undefined,
typeDynamicImports: undefined,
namedReexports: undefined,
namespaceReexports: undefined,
starReexports: undefined,
typeNamedReexports: undefined,
typeNamespaceReexports: undefined,
typeStarReexports: undefined,
defaultExport: undefined,
namedExports: undefined,
declarationExports: undefined,
typeNamedExports: undefined,
typeExports: undefined,
interfaceExports: undefined,
namespaceExports: undefined,
commonJsNamespaceExport: undefined,
commonJsExports: undefined,
errors: undefined,
[CONTEXT_KEY]: { lineColumnCache: undefined, linesIndexes: undefined, options, source },
};
var parse = parseCache[cacheKey];
if (parse === undefined) {
const statements = getStatements(options);
parse = createParseFunction({ ...baseParseOptions, statements });
parseCache[cacheKey] = parse;
}
parse(importsExports, source);
removeErrorsCausedByOverloading(importsExports);
importsExports[CONTEXT_KEY] = undefined;
return importsExports;
};
/**
* Base options of parse function.
*/
const baseParseOptions = { comments, onError: onGlobalError };
/**
* Cache of parse functions with different options.
*/
const parseCache = { __proto__: null };

View file

@ -0,0 +1,7 @@
import type { ImportsExports, Options } from './types';
/**
* Parses `import`/`export` in ECMAScript/TypeScript syntax.
*/
export declare const parseImportsExports: (source: string, options?: Options) => ImportsExports;
export type { ImportsExports, Options };
export type { Kind, LineColumn, Name, Path, Position, With } from './types';

View file

@ -0,0 +1,53 @@
import { onGlobalError } from './onErrors.js';
import { comments, getStatements } from './statements.js';
import { CONTEXT_KEY, createParseFunction, getCacheKey, removeErrorsCausedByOverloading, } from './utils.js';
/**
* Parses `import`/`export` in ECMAScript/TypeScript syntax.
*/
export const parseImportsExports = (source, options) => {
const cacheKey = getCacheKey(options);
const importsExports = {
namedImports: undefined,
namespaceImports: undefined,
dynamicImports: undefined,
requires: undefined,
typeNamedImports: undefined,
typeNamespaceImports: undefined,
typeDynamicImports: undefined,
namedReexports: undefined,
namespaceReexports: undefined,
starReexports: undefined,
typeNamedReexports: undefined,
typeNamespaceReexports: undefined,
typeStarReexports: undefined,
defaultExport: undefined,
namedExports: undefined,
declarationExports: undefined,
typeNamedExports: undefined,
typeExports: undefined,
interfaceExports: undefined,
namespaceExports: undefined,
commonJsNamespaceExport: undefined,
commonJsExports: undefined,
errors: undefined,
[CONTEXT_KEY]: { lineColumnCache: undefined, linesIndexes: undefined, options, source },
};
var parse = parseCache[cacheKey];
if (parse === undefined) {
const statements = getStatements(options);
parse = createParseFunction({ ...baseParseOptions, statements });
parseCache[cacheKey] = parse;
}
parse(importsExports, source);
removeErrorsCausedByOverloading(importsExports);
importsExports[CONTEXT_KEY] = undefined;
return importsExports;
};
/**
* Base options of parse function.
*/
const baseParseOptions = { comments, onError: onGlobalError };
/**
* Cache of parse functions with different options.
*/
const parseCache = { __proto__: null };

View file

@ -0,0 +1,125 @@
'use strict';
exports.onNamedExportError = undefined;
exports.onNamedExportParse = undefined;
const { parseFrom, parseWith } = require('./partParsers.cjs');
const { addError, getPosition, spacesRegExp, stripComments } = require('./utils.cjs');
/**
* Adds error of parsing named `export` statement.
*/
const onNamedExportError = exports.onNamedExportError = (importsExports, _source, { start, end, match: { groups } }) => addError(importsExports, `Cannot find end of \`export ${groups['type'] === undefined ? '' : 'type '}{...} ...\` statement`, start, end);
/**
* Parses named `export` statement.
*/
const onNamedExportParse = exports.onNamedExportParse = (importsExports, source, { start, end: unparsedStart, comments, match: startMatch }, { start: unparsedEnd, end: exportEnd, match: endMatch }) => {
var _a;
var end = exportEnd;
var maybeFrom;
var unparsed = stripComments(source, unparsedStart, unparsedEnd, comments);
const { groups } = endMatch;
const quote = (_a = groups.quote) !== null && _a !== void 0 ? _a : groups.with;
var isType = false;
if (startMatch.groups['type'] !== undefined) {
isType = true;
}
var withAttributes;
if (quote !== undefined) {
const { from, index } = parseFrom(quote[0], unparsed);
if (index === -1) {
return addError(importsExports, 'Cannot find start of `from` string literal of reexport', start, end);
}
maybeFrom = from;
unparsed = unparsed.slice(0, index);
const braceCloseIndex = unparsed.lastIndexOf('}');
if (braceCloseIndex === -1) {
return addError(importsExports, `Cannot find end of reexports list (\`}\`) for reexport from \`${maybeFrom}\``, start, end);
}
unparsed = unparsed.slice(0, braceCloseIndex);
if (groups.with !== undefined) {
if (isType) {
return addError(importsExports, `Cannot use import attributes (\`with {...}\`) in \`export type\` statement for reexport from \`${from}\``, start, end);
}
const attributes = parseWith(exportEnd, source);
if (attributes === undefined) {
return addError(importsExports, `Cannot find end of import attributes (\`with {...}\`) for reexport from \`${from}\``, start, end);
}
end = attributes.endIndex;
withAttributes = attributes.with;
if (withAttributes === undefined) {
return addError(importsExports, `Cannot parse import attributes (\`with {...}\`) for reexport from \`${from}\``, start, end);
}
}
}
const namedExport = getPosition(importsExports, start, end);
const namesString = unparsed.trim().replace(spacesRegExp, ' ');
const namesList = namesString.split(',');
var names;
var types;
for (let name of namesList) {
let isTypeName = false;
name = name.trim();
if (name === '') {
continue;
}
const nameObject = {};
if (name.startsWith('type ')) {
if (isType) {
return addError(importsExports, `Cannot use \`type\` modifier in \`export type {...}\` statement for type \`${name.slice(5)}\`${maybeFrom === undefined ? '' : ` for reexport from \`${maybeFrom}\``}`, start, end);
}
isTypeName = true;
name = name.slice(5);
}
const asIndex = name.indexOf(' as ');
if (asIndex !== -1) {
nameObject.by = name.slice(0, asIndex);
name = name.slice(asIndex + 4);
}
if (isTypeName) {
if (types === undefined) {
types = { __proto__: null };
}
else if (name in types) {
return addError(importsExports, `Duplicate exported type \`${name}\` ${maybeFrom === undefined ? 'in named export' : `for reexport from \`${maybeFrom}\``}`, start, end);
}
types[name] = nameObject;
}
else {
if (names === undefined) {
names = { __proto__: null };
}
else if (name in names) {
return addError(importsExports, `Duplicate exported name \`${name}\` ${maybeFrom === undefined ? 'in named export' : `for reexport from \`${maybeFrom}\``}`, start, end);
}
names[name] = nameObject;
}
}
if (names !== undefined) {
namedExport.names = names;
}
if (types !== undefined) {
namedExport.types = types;
}
if (maybeFrom === undefined) {
const key = isType ? 'typeNamedExports' : 'namedExports';
let exports = importsExports[key];
if (exports === undefined) {
importsExports[key] = [namedExport];
}
else {
exports.push(namedExport);
}
}
else {
const key = isType ? 'typeNamedReexports' : 'namedReexports';
const namedReexport = withAttributes === undefined ? namedExport : { ...namedExport, with: withAttributes };
let reexports = importsExports[key];
reexports !== null && reexports !== void 0 ? reexports : (reexports = importsExports[key] = { __proto__: null });
let reexportsList = reexports[maybeFrom];
if (reexportsList === undefined) {
reexports[maybeFrom] = [namedReexport];
}
else {
reexportsList.push(namedReexport);
}
}
return end;
};

View file

@ -0,0 +1,122 @@
import { parseFrom, parseWith } from './partParsers.js';
import { addError, getPosition, spacesRegExp, stripComments } from './utils.js';
/**
* Adds error of parsing named `export` statement.
*/
export const onNamedExportError = (importsExports, _source, { start, end, match: { groups } }) => addError(importsExports, `Cannot find end of \`export ${groups['type'] === undefined ? '' : 'type '}{...} ...\` statement`, start, end);
/**
* Parses named `export` statement.
*/
export const onNamedExportParse = (importsExports, source, { start, end: unparsedStart, comments, match: startMatch }, { start: unparsedEnd, end: exportEnd, match: endMatch }) => {
var _a;
var end = exportEnd;
var maybeFrom;
var unparsed = stripComments(source, unparsedStart, unparsedEnd, comments);
const { groups } = endMatch;
const quote = (_a = groups.quote) !== null && _a !== void 0 ? _a : groups.with;
var isType = false;
if (startMatch.groups['type'] !== undefined) {
isType = true;
}
var withAttributes;
if (quote !== undefined) {
const { from, index } = parseFrom(quote[0], unparsed);
if (index === -1) {
return addError(importsExports, 'Cannot find start of `from` string literal of reexport', start, end);
}
maybeFrom = from;
unparsed = unparsed.slice(0, index);
const braceCloseIndex = unparsed.lastIndexOf('}');
if (braceCloseIndex === -1) {
return addError(importsExports, `Cannot find end of reexports list (\`}\`) for reexport from \`${maybeFrom}\``, start, end);
}
unparsed = unparsed.slice(0, braceCloseIndex);
if (groups.with !== undefined) {
if (isType) {
return addError(importsExports, `Cannot use import attributes (\`with {...}\`) in \`export type\` statement for reexport from \`${from}\``, start, end);
}
const attributes = parseWith(exportEnd, source);
if (attributes === undefined) {
return addError(importsExports, `Cannot find end of import attributes (\`with {...}\`) for reexport from \`${from}\``, start, end);
}
end = attributes.endIndex;
withAttributes = attributes.with;
if (withAttributes === undefined) {
return addError(importsExports, `Cannot parse import attributes (\`with {...}\`) for reexport from \`${from}\``, start, end);
}
}
}
const namedExport = getPosition(importsExports, start, end);
const namesString = unparsed.trim().replace(spacesRegExp, ' ');
const namesList = namesString.split(',');
var names;
var types;
for (let name of namesList) {
let isTypeName = false;
name = name.trim();
if (name === '') {
continue;
}
const nameObject = {};
if (name.startsWith('type ')) {
if (isType) {
return addError(importsExports, `Cannot use \`type\` modifier in \`export type {...}\` statement for type \`${name.slice(5)}\`${maybeFrom === undefined ? '' : ` for reexport from \`${maybeFrom}\``}`, start, end);
}
isTypeName = true;
name = name.slice(5);
}
const asIndex = name.indexOf(' as ');
if (asIndex !== -1) {
nameObject.by = name.slice(0, asIndex);
name = name.slice(asIndex + 4);
}
if (isTypeName) {
if (types === undefined) {
types = { __proto__: null };
}
else if (name in types) {
return addError(importsExports, `Duplicate exported type \`${name}\` ${maybeFrom === undefined ? 'in named export' : `for reexport from \`${maybeFrom}\``}`, start, end);
}
types[name] = nameObject;
}
else {
if (names === undefined) {
names = { __proto__: null };
}
else if (name in names) {
return addError(importsExports, `Duplicate exported name \`${name}\` ${maybeFrom === undefined ? 'in named export' : `for reexport from \`${maybeFrom}\``}`, start, end);
}
names[name] = nameObject;
}
}
if (names !== undefined) {
namedExport.names = names;
}
if (types !== undefined) {
namedExport.types = types;
}
if (maybeFrom === undefined) {
const key = isType ? 'typeNamedExports' : 'namedExports';
let exports = importsExports[key];
if (exports === undefined) {
importsExports[key] = [namedExport];
}
else {
exports.push(namedExport);
}
}
else {
const key = isType ? 'typeNamedReexports' : 'namedReexports';
const namedReexport = withAttributes === undefined ? namedExport : { ...namedExport, with: withAttributes };
let reexports = importsExports[key];
reexports !== null && reexports !== void 0 ? reexports : (reexports = importsExports[key] = { __proto__: null });
let reexportsList = reexports[maybeFrom];
if (reexportsList === undefined) {
reexports[maybeFrom] = [namedReexport];
}
else {
reexportsList.push(namedReexport);
}
}
return end;
};

View file

@ -0,0 +1,27 @@
'use strict';
exports.onDoubleQuoteError = undefined;
exports.onGlobalError = undefined;
exports.onMultilineCommentError = undefined;
exports.onSinglelineCommentError = undefined;
exports.onSingleQuoteError = undefined;
const { addError } = require('./utils.cjs');
/**
* Adds error of parsing string literal started with double quote.
*/
const onDoubleQuoteError = exports.onDoubleQuoteError = (importsExports, _source, { start, end }) => addError(importsExports, 'Cannot find end of string literal started with double quote', start, end);
/**
* Adds global error of parsing source.
*/
const onGlobalError = exports.onGlobalError = (importsExports, _source, message, index) => addError(importsExports, message, index);
/**
* Adds error of parsing multiline comment.
*/
const onMultilineCommentError = exports.onMultilineCommentError = (importsExports, _source, { start }) => addError(importsExports, 'Cannot find end of multiline comment', start);
/**
* Adds error of parsing single line comment.
*/
const onSinglelineCommentError = exports.onSinglelineCommentError = (importsExports, _source, { start }) => addError(importsExports, 'Cannot find end of single line comment', start);
/**
* Adds error of parsing string literal started with single quote.
*/
const onSingleQuoteError = exports.onSingleQuoteError = (importsExports, _source, { start, end }) => addError(importsExports, 'Cannot find end of string literal started with single quote', start, end);

View file

@ -0,0 +1,21 @@
import { addError } from './utils.js';
/**
* Adds error of parsing string literal started with double quote.
*/
export const onDoubleQuoteError = (importsExports, _source, { start, end }) => addError(importsExports, 'Cannot find end of string literal started with double quote', start, end);
/**
* Adds global error of parsing source.
*/
export const onGlobalError = (importsExports, _source, message, index) => addError(importsExports, message, index);
/**
* Adds error of parsing multiline comment.
*/
export const onMultilineCommentError = (importsExports, _source, { start }) => addError(importsExports, 'Cannot find end of multiline comment', start);
/**
* Adds error of parsing single line comment.
*/
export const onSinglelineCommentError = (importsExports, _source, { start }) => addError(importsExports, 'Cannot find end of single line comment', start);
/**
* Adds error of parsing string literal started with single quote.
*/
export const onSingleQuoteError = (importsExports, _source, { start, end }) => addError(importsExports, 'Cannot find end of string literal started with single quote', start, end);

View file

@ -0,0 +1,54 @@
{
"name": "parse-imports-exports",
"version": "0.2.4",
"description": "Fast and easy parser for declarations of import and export in ECMAScript/TypeScript",
"author": "uid11",
"bugs": "https://github.com/joomcode/parse-imports-exports/issues",
"dependencies": {
"parse-statements": "1.0.11"
},
"devDependencies": {
"prettier": ">=3.5",
"typescript": ">=5.7"
},
"exports": {
".": {
"import": "./index.js",
"require": "./index.cjs",
"types": "./index.d.ts"
}
},
"files": [
"*.cjs",
"*.js",
"index.d.ts",
"types.d.ts"
],
"homepage": "https://github.com/joomcode/parse-imports-exports#readme",
"keywords": [
"esm",
"export",
"import",
"parse",
"parser",
"typescript"
],
"license": "MIT",
"packageManager": "npm@10",
"repository": {
"type": "git",
"url": "git+https://github.com/joomcode/parse-imports-exports.git"
},
"scripts": {
"prettier": "prettier --cache --cache-strategy=content --write .",
"prebuild": "rm -fr ./build/*",
"build": "tsc",
"postbuild": "node ./build/convertEsmToCjs.js",
"prerelease": "npm test && cp LICENSE package.json README.md ./build/src && rm ./build/src/types.cjs ./build/src/types.js",
"release": "npm publish ./build/src",
"test": "export _START=$(date +%s%3N) && npm run prettier && npm run build && node ./build/spec/index.spec.js"
},
"sideEffects": false,
"type": "module",
"types": "./index.d.ts"
}

View file

@ -0,0 +1,174 @@
'use strict';
exports.parseDestructuring = undefined;
exports.parseFrom = undefined;
exports.parseIdentifier = undefined;
exports.parseWith = undefined;
/**
* Parses destructuring assignment.
*/
const parseDestructuring = exports.parseDestructuring = (sourceStartsWithDestructuring) => {
const openBracket = sourceStartsWithDestructuring[0];
const closeBracket = openBracket === '{' ? '}' : ']';
const names = [];
var bracketsDepth = 1;
var index = 1;
var isInsideSinglelineComment = false;
var isInsideMultilineComment = false;
for (; index < sourceStartsWithDestructuring.length; index += 1) {
const char = sourceStartsWithDestructuring[index];
if (isInsideSinglelineComment) {
if (char === '\n') {
isInsideSinglelineComment = false;
}
continue;
}
else if (isInsideMultilineComment) {
if (char === '*' && sourceStartsWithDestructuring[index + 1] === '/') {
isInsideMultilineComment = false;
index += 1;
}
continue;
}
if (char === openBracket) {
bracketsDepth += 1;
}
else if (char === closeBracket) {
bracketsDepth -= 1;
if (bracketsDepth === 0) {
return { endIndex: index, names };
}
}
else if (char === ':') {
names.pop();
}
else if (char === '/') {
const nextChar = sourceStartsWithDestructuring[index + 1];
if (nextChar === '/') {
isInsideSinglelineComment = true;
index += 1;
}
else if (nextChar === '*') {
isInsideMultilineComment = true;
index += 1;
}
}
else if (char in identifierStartCharacters) {
const nameIndex = parseIdentifier(sourceStartsWithDestructuring.slice(index));
if (nameIndex === 0) {
return;
}
names.push(sourceStartsWithDestructuring.slice(index, index + nameIndex));
index += nameIndex - 1;
}
}
return;
};
/**
* Parses string literal after `from` at the end of the source string.
*/
const parseFrom = exports.parseFrom = (quoteCharacter, sourceWithString) => {
var hasBackslash = false;
var index = sourceWithString.length - 1;
for (; index !== -1; index -= 1) {
const char = sourceWithString[index];
if (char === '\\') {
hasBackslash = true;
}
if (char === quoteCharacter && sourceWithString[index - 1] !== '\\') {
break;
}
}
var from = sourceWithString.slice(index + 1);
if (hasBackslash) {
from = from.replace(backslashesRegExp, stripFirstCharacter);
}
return { from, index };
};
/**
* Parses identifier from the start of the source string.
*/
const parseIdentifier = exports.parseIdentifier = (sourceStartsWithIdentifier) => {
if (!identifierStartCharacters[sourceStartsWithIdentifier[0]]) {
return 0;
}
for (let index = 1; index < sourceStartsWithIdentifier.length; index += 1) {
if (!identifierCharacters[sourceStartsWithIdentifier[index]]) {
return index;
}
}
return sourceStartsWithIdentifier.length;
};
/**
* Parses import/reexport attributes in `with`-part of import/reexport (like `with { type: "json" }`).
*/
const parseWith = exports.parseWith = (startIndex, source) => {
const endIndex = source.indexOf('}', startIndex);
if (endIndex === -1) {
return;
}
const withAttributes = { __proto__: null };
const attributesSource = source.slice(startIndex, endIndex).trim();
if (attributesSource === '') {
return { endIndex, with: withAttributes };
}
const attributes = attributesSource.split(',');
for (const attribute of attributes) {
const parts = attribute.split(':');
if (parts.length !== 2) {
return { endIndex };
}
let key = parts[0].trim();
if (key[0] === "'" || key[0] === '"') {
key = trimQuotes(key);
}
if (!key) {
return { endIndex };
}
const value = trimQuotes(parts[1].trim());
if (!value || key in withAttributes) {
return { endIndex };
}
withAttributes[key] = value;
}
return { endIndex, with: withAttributes };
};
/**
* Regexp that find all backslashes.
*/
const backslashesRegExp = /\\+/g;
/**
* Hash with start characters of identifiers.
*/
const identifierStartCharacters = { __proto__: null };
for (const character of 'abcdefghijklmnopqrstuvwxyz_$') {
identifierStartCharacters[character] = true;
identifierStartCharacters[character.toUpperCase()] = true;
}
/**
* Hash with characters of identifiers.
*/
const identifierCharacters = { __proto__: null, ...identifierStartCharacters };
for (const character of '0123456789') {
identifierCharacters[character] = true;
}
/**
* Strips first character from string.
*/
const stripFirstCharacter = (someString) => someString.slice(1);
/**
* Trims single or double quotes around the string value.
* If string is not quoted, returns `undefined`.
*/
const trimQuotes = (value) => {
if (value.length < 2) {
return;
}
var firstChar = value[0];
if (firstChar !== "'" && firstChar !== '"') {
return;
}
if (firstChar !== value[value.length - 1]) {
return;
}
return value.slice(1, -1);
};

View file

@ -0,0 +1,169 @@
/**
* Parses destructuring assignment.
*/
export const parseDestructuring = (sourceStartsWithDestructuring) => {
const openBracket = sourceStartsWithDestructuring[0];
const closeBracket = openBracket === '{' ? '}' : ']';
const names = [];
var bracketsDepth = 1;
var index = 1;
var isInsideSinglelineComment = false;
var isInsideMultilineComment = false;
for (; index < sourceStartsWithDestructuring.length; index += 1) {
const char = sourceStartsWithDestructuring[index];
if (isInsideSinglelineComment) {
if (char === '\n') {
isInsideSinglelineComment = false;
}
continue;
}
else if (isInsideMultilineComment) {
if (char === '*' && sourceStartsWithDestructuring[index + 1] === '/') {
isInsideMultilineComment = false;
index += 1;
}
continue;
}
if (char === openBracket) {
bracketsDepth += 1;
}
else if (char === closeBracket) {
bracketsDepth -= 1;
if (bracketsDepth === 0) {
return { endIndex: index, names };
}
}
else if (char === ':') {
names.pop();
}
else if (char === '/') {
const nextChar = sourceStartsWithDestructuring[index + 1];
if (nextChar === '/') {
isInsideSinglelineComment = true;
index += 1;
}
else if (nextChar === '*') {
isInsideMultilineComment = true;
index += 1;
}
}
else if (char in identifierStartCharacters) {
const nameIndex = parseIdentifier(sourceStartsWithDestructuring.slice(index));
if (nameIndex === 0) {
return;
}
names.push(sourceStartsWithDestructuring.slice(index, index + nameIndex));
index += nameIndex - 1;
}
}
return;
};
/**
* Parses string literal after `from` at the end of the source string.
*/
export const parseFrom = (quoteCharacter, sourceWithString) => {
var hasBackslash = false;
var index = sourceWithString.length - 1;
for (; index !== -1; index -= 1) {
const char = sourceWithString[index];
if (char === '\\') {
hasBackslash = true;
}
if (char === quoteCharacter && sourceWithString[index - 1] !== '\\') {
break;
}
}
var from = sourceWithString.slice(index + 1);
if (hasBackslash) {
from = from.replace(backslashesRegExp, stripFirstCharacter);
}
return { from, index };
};
/**
* Parses identifier from the start of the source string.
*/
export const parseIdentifier = (sourceStartsWithIdentifier) => {
if (!identifierStartCharacters[sourceStartsWithIdentifier[0]]) {
return 0;
}
for (let index = 1; index < sourceStartsWithIdentifier.length; index += 1) {
if (!identifierCharacters[sourceStartsWithIdentifier[index]]) {
return index;
}
}
return sourceStartsWithIdentifier.length;
};
/**
* Parses import/reexport attributes in `with`-part of import/reexport (like `with { type: "json" }`).
*/
export const parseWith = (startIndex, source) => {
const endIndex = source.indexOf('}', startIndex);
if (endIndex === -1) {
return;
}
const withAttributes = { __proto__: null };
const attributesSource = source.slice(startIndex, endIndex).trim();
if (attributesSource === '') {
return { endIndex, with: withAttributes };
}
const attributes = attributesSource.split(',');
for (const attribute of attributes) {
const parts = attribute.split(':');
if (parts.length !== 2) {
return { endIndex };
}
let key = parts[0].trim();
if (key[0] === "'" || key[0] === '"') {
key = trimQuotes(key);
}
if (!key) {
return { endIndex };
}
const value = trimQuotes(parts[1].trim());
if (!value || key in withAttributes) {
return { endIndex };
}
withAttributes[key] = value;
}
return { endIndex, with: withAttributes };
};
/**
* Regexp that find all backslashes.
*/
const backslashesRegExp = /\\+/g;
/**
* Hash with start characters of identifiers.
*/
const identifierStartCharacters = { __proto__: null };
for (const character of 'abcdefghijklmnopqrstuvwxyz_$') {
identifierStartCharacters[character] = true;
identifierStartCharacters[character.toUpperCase()] = true;
}
/**
* Hash with characters of identifiers.
*/
const identifierCharacters = { __proto__: null, ...identifierStartCharacters };
for (const character of '0123456789') {
identifierCharacters[character] = true;
}
/**
* Strips first character from string.
*/
const stripFirstCharacter = (someString) => someString.slice(1);
/**
* Trims single or double quotes around the string value.
* If string is not quoted, returns `undefined`.
*/
const trimQuotes = (value) => {
if (value.length < 2) {
return;
}
var firstChar = value[0];
if (firstChar !== "'" && firstChar !== '"') {
return;
}
if (firstChar !== value[value.length - 1]) {
return;
}
return value.slice(1, -1);
};

View file

@ -0,0 +1,20 @@
'use strict';
exports.onRegexpError = undefined;
exports.onRegexpParse = undefined;
const { addError } = require('./utils.cjs');
/**
* Adds error of parsing regexp literal.
*/
const onRegexpError = exports.onRegexpError = (importsExports, _source, { start, end }) => addError(importsExports, 'Cannot find end of regexp literal', start, end);
/**
* Parses `/.../` statement (regular expression literal).
*/
const onRegexpParse = exports.onRegexpParse = (_importsExports, source, parsedToken, { end, token }) => {
if (token !== '/') {
return parsedToken.end;
}
if (source[end] === '*') {
return end - 1;
}
return;
};

View file

@ -0,0 +1,17 @@
import { addError } from './utils.js';
/**
* Adds error of parsing regexp literal.
*/
export const onRegexpError = (importsExports, _source, { start, end }) => addError(importsExports, 'Cannot find end of regexp literal', start, end);
/**
* Parses `/.../` statement (regular expression literal).
*/
export const onRegexpParse = (_importsExports, source, parsedToken, { end, token }) => {
if (token !== '/') {
return parsedToken.end;
}
if (source[end] === '*') {
return end - 1;
}
return;
};

View file

@ -0,0 +1,33 @@
'use strict';
exports.onRequireError = undefined;
exports.onRequireParse = undefined;
const { parseFrom } = require('./partParsers.cjs');
const { addError, getPosition } = require('./utils.cjs');
/**
* Adds error of parsing `require('...')`/`require("...")` statement.
*/
const onRequireError = exports.onRequireError = (importsExports, _source, { start, end }) => addError(importsExports, 'Cannot find end of `require(...)` statement', start, end);
/**
* Parses `require('...')`/`require("...")` statement.
*/
const onRequireParse = exports.onRequireParse = (importsExports, source, { start }, { start: unparsedStart }, { start: unparsedEnd, end, token: endToken }) => {
const unparsed = source.slice(unparsedStart, unparsedEnd);
const quoteCharacter = endToken[0];
if (quoteCharacter === undefined) {
return addError(importsExports, 'Cannot find end of path string literal in `require(...)`', start, end);
}
const { from, index } = parseFrom(quoteCharacter, unparsed);
if (index !== 0) {
return addError(importsExports, `Cannot find start of path string literal in \`require(${quoteCharacter}...${quoteCharacter})\``, start, end);
}
const parsedRequire = getPosition(importsExports, start, end);
var { requires } = importsExports;
requires !== null && requires !== void 0 ? requires : (requires = importsExports.requires = { __proto__: null });
var requiresList = requires[from];
if (requiresList === undefined) {
requires[from] = [parsedRequire];
}
else {
requiresList.push(parsedRequire);
}
};

View file

@ -0,0 +1,30 @@
import { parseFrom } from './partParsers.js';
import { addError, getPosition } from './utils.js';
/**
* Adds error of parsing `require('...')`/`require("...")` statement.
*/
export const onRequireError = (importsExports, _source, { start, end }) => addError(importsExports, 'Cannot find end of `require(...)` statement', start, end);
/**
* Parses `require('...')`/`require("...")` statement.
*/
export const onRequireParse = (importsExports, source, { start }, { start: unparsedStart }, { start: unparsedEnd, end, token: endToken }) => {
const unparsed = source.slice(unparsedStart, unparsedEnd);
const quoteCharacter = endToken[0];
if (quoteCharacter === undefined) {
return addError(importsExports, 'Cannot find end of path string literal in `require(...)`', start, end);
}
const { from, index } = parseFrom(quoteCharacter, unparsed);
if (index !== 0) {
return addError(importsExports, `Cannot find start of path string literal in \`require(${quoteCharacter}...${quoteCharacter})\``, start, end);
}
const parsedRequire = getPosition(importsExports, start, end);
var { requires } = importsExports;
requires !== null && requires !== void 0 ? requires : (requires = importsExports.requires = { __proto__: null });
var requiresList = requires[from];
if (requiresList === undefined) {
requires[from] = [parsedRequire];
}
else {
requiresList.push(parsedRequire);
}
};

View file

@ -0,0 +1,137 @@
'use strict';
exports.comments = undefined;
exports.getStatements = undefined;
const { onBacktickError, onBacktickParse } = require('./backtickString.cjs');
const { onCommonJsExportError, onCommonJsExportParse } = require('./commonJsExport.cjs');
const { onDeclarationExportError, onDeclarationExportParse } = require('./declarationExport.cjs');
const { onDynamicImportError, onDynamicImportParse } = require('./dynamicImport.cjs');
const { onImportError, onImportParse } = require('./import.cjs');
const { onNamedExportError, onNamedExportParse } = require('./namedExport.cjs');
const { onDoubleQuoteError, onMultilineCommentError, onSinglelineCommentError, onSingleQuoteError, } = require('./onErrors.cjs');
const { onRegexpError, onRegexpParse } = require('./regexp.cjs');
const { onRequireError, onRequireParse } = require('./require.cjs');
/**
* Statements of ECMAScript comments.
*/
const comments = exports.comments = [
{
onError: onSinglelineCommentError,
tokens: ['\\/\\/', '$'],
},
{
onError: onMultilineCommentError,
tokens: ['\\/\\*', '\\*\\/'],
},
];
/**
* Get statements for parsing by options.
*/
const getStatements = exports.getStatements = (options) => {
const statements = [...baseStatements];
if (!(options === null || options === void 0 ? void 0 : options.ignoreDynamicImports)) {
statements.push(dynamicImportStatement);
}
if (!(options === null || options === void 0 ? void 0 : options.ignoreRequires)) {
statements.push(requireStatement);
}
if (!(options === null || options === void 0 ? void 0 : options.ignoreCommonJsExports)) {
statements.push(commonJsExportStatement);
}
if (!(options === null || options === void 0 ? void 0 : options.ignoreRegexpLiterals)) {
statements.push(regexpLiteralStatement);
}
if (!(options === null || options === void 0 ? void 0 : options.ignoreStringLiterals)) {
statements.unshift(...stringLiteralStatements);
}
return statements;
};
/**
* Base statements for parsing `import`/`export` declarations.
*/
const baseStatements = [
{
canIncludeComments: true,
onError: onImportError,
onParse: onImportParse,
tokens: ['^import ', '([\'"];?$)|(?<with>[\'"] with \\{)'],
shouldSearchBeforeComments: true,
},
{
canIncludeComments: true,
onError: onNamedExportError,
onParse: onNamedExportParse,
tokens: ['^export (?<type>type )?\\{', '(\\};?$)|(?<quote>[\'"];?$)|(?<with>[\'"] with \\{)'],
shouldSearchBeforeComments: true,
},
{
canIncludeComments: true,
onError: onDeclarationExportError,
onParse: onDeclarationExportParse,
tokens: ['^export ', '($)|(?<with>[\'"] with \\{)'],
shouldSearchBeforeComments: true,
},
];
/**
* Statement for parsing CommonJS exports (`module.exports = ...`/`(module.)exports.foo = ...`).
*/
const commonJsExportStatement = {
canIncludeComments: true,
onError: onCommonJsExportError,
onParse: onCommonJsExportParse,
tokens: ['^(module\\.)?exports\\b', '='],
shouldSearchBeforeComments: true,
};
/**
* Statement for parsing dynamic import call (`import(...)`).
*/
const dynamicImportStatement = {
canIncludeComments: true,
onError: onDynamicImportError,
onParse: onDynamicImportParse,
tokens: ['\\bimport\\(', '[\'"]', '([\'"])|($)'],
shouldSearchBeforeComments: true,
};
/**
* Statement for parsing regexp literal (`/.../`).
*/
const regexpLiteralStatement = {
canIncludeComments: false,
onError: onRegexpError,
onParse: onRegexpParse,
tokens: ['/', '((?<!\\\\)/)|($)'],
shouldSearchBeforeComments: false,
};
/**
* Statement for parsing require call (`require(...)`).
*/
const requireStatement = {
canIncludeComments: true,
onError: onRequireError,
onParse: onRequireParse,
tokens: ['\\brequire\\(', '[\'"]', '([\'"])|($)'],
shouldSearchBeforeComments: true,
};
/**
* Statements for parsing string literals.
*/
const stringLiteralStatements = [
{
canIncludeComments: false,
onError: onSingleQuoteError,
tokens: ["'", "((?<!\\\\)')|($)"],
shouldSearchBeforeComments: true,
},
{
canIncludeComments: false,
onError: onDoubleQuoteError,
tokens: ['"', '((?<!\\\\)")|($)'],
shouldSearchBeforeComments: true,
},
{
canIncludeComments: false,
onError: onBacktickError,
onParse: onBacktickParse,
tokens: ['`', '(?<!\\\\)`'],
shouldSearchBeforeComments: true,
},
];

View file

@ -0,0 +1,134 @@
import { onBacktickError, onBacktickParse } from './backtickString.js';
import { onCommonJsExportError, onCommonJsExportParse } from './commonJsExport.js';
import { onDeclarationExportError, onDeclarationExportParse } from './declarationExport.js';
import { onDynamicImportError, onDynamicImportParse } from './dynamicImport.js';
import { onImportError, onImportParse } from './import.js';
import { onNamedExportError, onNamedExportParse } from './namedExport.js';
import { onDoubleQuoteError, onMultilineCommentError, onSinglelineCommentError, onSingleQuoteError, } from './onErrors.js';
import { onRegexpError, onRegexpParse } from './regexp.js';
import { onRequireError, onRequireParse } from './require.js';
/**
* Statements of ECMAScript comments.
*/
export const comments = [
{
onError: onSinglelineCommentError,
tokens: ['\\/\\/', '$'],
},
{
onError: onMultilineCommentError,
tokens: ['\\/\\*', '\\*\\/'],
},
];
/**
* Get statements for parsing by options.
*/
export const getStatements = (options) => {
const statements = [...baseStatements];
if (!(options === null || options === void 0 ? void 0 : options.ignoreDynamicImports)) {
statements.push(dynamicImportStatement);
}
if (!(options === null || options === void 0 ? void 0 : options.ignoreRequires)) {
statements.push(requireStatement);
}
if (!(options === null || options === void 0 ? void 0 : options.ignoreCommonJsExports)) {
statements.push(commonJsExportStatement);
}
if (!(options === null || options === void 0 ? void 0 : options.ignoreRegexpLiterals)) {
statements.push(regexpLiteralStatement);
}
if (!(options === null || options === void 0 ? void 0 : options.ignoreStringLiterals)) {
statements.unshift(...stringLiteralStatements);
}
return statements;
};
/**
* Base statements for parsing `import`/`export` declarations.
*/
const baseStatements = [
{
canIncludeComments: true,
onError: onImportError,
onParse: onImportParse,
tokens: ['^import ', '([\'"];?$)|(?<with>[\'"] with \\{)'],
shouldSearchBeforeComments: true,
},
{
canIncludeComments: true,
onError: onNamedExportError,
onParse: onNamedExportParse,
tokens: ['^export (?<type>type )?\\{', '(\\};?$)|(?<quote>[\'"];?$)|(?<with>[\'"] with \\{)'],
shouldSearchBeforeComments: true,
},
{
canIncludeComments: true,
onError: onDeclarationExportError,
onParse: onDeclarationExportParse,
tokens: ['^export ', '($)|(?<with>[\'"] with \\{)'],
shouldSearchBeforeComments: true,
},
];
/**
* Statement for parsing CommonJS exports (`module.exports = ...`/`(module.)exports.foo = ...`).
*/
const commonJsExportStatement = {
canIncludeComments: true,
onError: onCommonJsExportError,
onParse: onCommonJsExportParse,
tokens: ['^(module\\.)?exports\\b', '='],
shouldSearchBeforeComments: true,
};
/**
* Statement for parsing dynamic import call (`import(...)`).
*/
const dynamicImportStatement = {
canIncludeComments: true,
onError: onDynamicImportError,
onParse: onDynamicImportParse,
tokens: ['\\bimport\\(', '[\'"]', '([\'"])|($)'],
shouldSearchBeforeComments: true,
};
/**
* Statement for parsing regexp literal (`/.../`).
*/
const regexpLiteralStatement = {
canIncludeComments: false,
onError: onRegexpError,
onParse: onRegexpParse,
tokens: ['/', '((?<!\\\\)/)|($)'],
shouldSearchBeforeComments: false,
};
/**
* Statement for parsing require call (`require(...)`).
*/
const requireStatement = {
canIncludeComments: true,
onError: onRequireError,
onParse: onRequireParse,
tokens: ['\\brequire\\(', '[\'"]', '([\'"])|($)'],
shouldSearchBeforeComments: true,
};
/**
* Statements for parsing string literals.
*/
const stringLiteralStatements = [
{
canIncludeComments: false,
onError: onSingleQuoteError,
tokens: ["'", "((?<!\\\\)')|($)"],
shouldSearchBeforeComments: true,
},
{
canIncludeComments: false,
onError: onDoubleQuoteError,
tokens: ['"', '((?<!\\\\)")|($)'],
shouldSearchBeforeComments: true,
},
{
canIncludeComments: false,
onError: onBacktickError,
onParse: onBacktickParse,
tokens: ['`', '(?<!\\\\)`'],
shouldSearchBeforeComments: true,
},
];

View file

@ -0,0 +1,373 @@
import type { Comment as BaseComment, Options as ParseOptions, Statement as BaseStatement } from 'parse-statements';
export type { CommentPair, OnCommentError, OnGlobalError, OnParse, Parse } from 'parse-statements';
/**
* Type of comment for parse options.
*/
export type Comment = BaseComment<MutableImportsExports>;
/**
* Internal context in `MutableImportsExports`.
*/
export type Context = {
lineColumnCache: Record<number, LineColumn> | undefined;
linesIndexes: readonly number[] | undefined;
readonly options: Options | undefined;
readonly source: string;
};
/**
* Type of `Context` key.
*/
export type ContextKey = typeof CONTEXT_KEY;
/**
* Parsed JSON presentation of `import(...)` statement.
*/
export type DynamicImport = Position & {
with?: With;
};
/**
* Excludes `undefined` from some `Type`.
*/
export type ExcludeUndefined<Type> = Exclude<Type, undefined>;
/**
* Parsed JSON presentation of imports, exports and reexports of ECMAScript/TypeScript module.
*/
export type ImportsExports = DeepReadonly<MutableImportsExportsWithoutContext>;
/**
* Parsed JSON presentation of `export interface ...` statement.
*/
export type InterfaceExport = Position & {
isDeclare?: true;
};
/**
* Kind of exported declaration.
*/
export type Kind = 'abstract class' | 'async function' | 'async function*' | 'class' | 'const' | 'const enum' | 'declare abstract class' | 'declare class' | 'declare const' | 'declare const enum' | 'declare destructuring const' | 'declare destructuring let' | 'declare destructuring var' | 'declare enum' | 'declare function' | 'declare let' | 'declare var' | 'destructuring const' | 'destructuring let' | 'destructuring var' | 'enum' | 'function' | 'function*' | 'let' | 'var';
/**
* String with position of symbol in source, like `3:12` -- line 3, column 12.
*/
export type LineColumn = Brand<string, 'LineColumn'>;
/**
* Internal presentation of imports, exports and reexports of module.
*/
export type MutableImportsExports = MutableImportsExportsWithoutContext & Readonly<{
[CONTEXT_KEY]: Context;
}>;
/**
* Imported or exported name (identifier).
*/
export type Name = Brand<string, 'Name'>;
/**
* Parsed JSON presentation of `export {...}` statement.
*/
export type NamedExport = Position & {
names?: Names;
types?: Names;
};
/**
* Parsed JSON presentation of `import {...} from ...` statement.
*/
export type NamedImport = Position & {
default?: Name;
names?: Names;
types?: Names;
with?: With;
};
/**
* Parsed JSON presentation of `export {...} from ...` statement.
*/
export type NamedReexport = Position & {
names?: Names;
types?: Names;
with?: With;
};
/**
* Parsed JSON presentation of names in `import`/`export` statements.
*/
export type Names = Record<Name, {
by?: Name;
}>;
/**
* Parsed JSON presentation of `import * as ...` statement.
*/
export type NamespaceImport = Position & {
default?: Name;
namespace: Name;
with?: With;
};
/**
* Parsed JSON presentation of `export * as ... from ...` statement.
*/
export type NamespaceReexport = Position & {
namespace: Name;
with?: With;
};
/**
* Options of `parseImportsExports` function.
*/
export type Options = Readonly<{
/**
* If `true`, then we ignore `module.exports = ...`/`(module.)exports.foo = ...` expressions
* during parsing (maybe a little faster).
* By default (if `false` or skipped option), `module.exports = ...`/`(module.)exports.foo = ...`
* expressions are parsed.
*/
ignoreCommonJsExports?: boolean;
/**
* If `true`, then we ignore `import(...)` expressions during parsing (maybe a little faster).
* By default (if `false` or skipped option), `import(...)` expressions are parsed.
*/
ignoreDynamicImports?: boolean;
/**
* If `true`, then we ignore regular expression literals (`/.../`)
* during parsing (maybe a little faster).
* By default (if `false` or skipped option), regular expression literals are parsed.
*/
ignoreRegexpLiterals?: boolean;
/**
* If `true`, then we ignore `require(...)` expressions during parsing (maybe a little faster).
* By default (if `false` or skipped option), `require(...)` expressions are parsed.
*/
ignoreRequires?: boolean;
/**
* If `true`, then we ignore string literals during parsing (maybe a little faster).
* By default (if `false` or skipped option), string literals are parsed, that is,
* the text inside them cannot be interpreted as another expression.
*/
ignoreStringLiterals?: boolean;
/**
* If `true`, then we include the token's start line-column and the token's end line-column
* in the token's position in source file.
* By default (if `false` or skipped option), the position includes only
* the token's start index and the token end index.
*/
includeLineColumn?: boolean;
}>;
/**
* Parsed JSON presentation of `export namespace ...` statement.
*/
export type NamespaceExport = Position & {
isDeclare?: true;
};
export type { ParseOptions };
/**
* Path to a module or package after the `from` keyword.
*/
export type Path = Brand<string, 'Path'>;
/**
* Position of import, export or reexport statement in source file.
*/
export type Position = {
start: number;
startLineColumn?: LineColumn;
end: number;
endLineColumn?: LineColumn;
};
/**
* Parsed JSON presentation of `require(...)` statement.
*/
export type Require = Position;
/**
* Parsed JSON presentation of `export * from ...` statement.
*/
export type StarReexport = Position & {
with?: With;
};
/**
* Type of statement for parse options.
*/
export type Statement = BaseStatement<MutableImportsExports>;
/**
* Parsed JSON presentation of `export type {...}` statement.
*/
export type TypeNamedExport = Position & {
names?: Names;
};
/**
* Parsed JSON presentation of `export type {...} from ...` statement.
*/
export type TypeNamedReexport = Position & {
names?: Names;
};
/**
* Import/reexport attributes in `with`-part of import/reexport (like `with { type: "json" }`).
*/
export type With = Record<string, string>;
/**
* Parsed JSON presentation of `(module.)exports.foo = ...` statement.
*/
type CommonJsExport = Position & {
startsWithModule?: true;
};
/**
* Inner key for brand types.
*/
declare const BRAND: unique symbol;
/**
* Creates brand (nominal) type from regular type.
*/
type Brand<Type, Key extends string> = Type & {
readonly [BRAND]: Key;
};
/**
* Parsed JSON presentation of `module.exports = ...` statement.
*/
type CommonJsNamespaceExport = Position;
/**
* Symbol for generation type of `Context` key.
*/
declare const CONTEXT_KEY: unique symbol;
/**
* Parsed JSON presentation of `export (class|const|function|var...) ...` statement.
*/
type DeclarationExport = Position & {
kind: Kind;
};
/**
* Parsed JSON presentation of `export default ...` statement.
*/
type DefaultExport = Position;
/**
* Readonly type with recursive applying.
* `DeepReadonly<{foo: {bar: 0}}>` = `{readonly foo: {readonly bar: 0}}`.
*/
type DeepReadonly<Type> = Type extends Brand<unknown, string> ? Type : {
readonly [Key in keyof Type]: DeepReadonly<Type[Key]>;
};
/**
* Mutable parsed JSON presentation of imports, exports and reexports of module.
*/
type MutableImportsExportsWithoutContext = {
/**
* Imports.
*/
/**
* `import {...} from ...`.
*/
namedImports: Record<Path, NotEmptyArray<NamedImport>> | undefined;
/**
* `import * as ...`.
*/
namespaceImports: Record<Path, NotEmptyArray<NamespaceImport>> | undefined;
/**
* `import(...)`.
*/
dynamicImports: Record<Path, NotEmptyArray<DynamicImport>> | undefined;
/**
* `require(...)`.
*/
requires: Record<Path, NotEmptyArray<Require>> | undefined;
/**
* `import type {...} from ...`.
*/
typeNamedImports: Record<Path, NotEmptyArray<TypeNamedImport>> | undefined;
/**
* `import type * as ...`.
*/
typeNamespaceImports: Record<Path, NotEmptyArray<TypeNamespaceImport>> | undefined;
/**
* `typeof import(...)`.
*/
typeDynamicImports: Record<Path, NotEmptyArray<DynamicImport>> | undefined;
/**
* Reexports.
*/
/**
* `export {...} from ...`.
*/
namedReexports: Record<Path, NotEmptyArray<NamedReexport>> | undefined;
/**
* `export * as ... from ...`.
*/
namespaceReexports: Record<Path, NotEmptyArray<NamespaceReexport>> | undefined;
/**
* `export * from ...`.
*/
starReexports: Record<Path, NotEmptyArray<StarReexport>> | undefined;
/**
* `export type {...} from ...`.
*/
typeNamedReexports: Record<Path, NotEmptyArray<TypeNamedReexport>> | undefined;
/**
* `export type * as ... from ...`.
*/
typeNamespaceReexports: Record<Path, NotEmptyArray<TypeNamespaceReexport>> | undefined;
/**
* `export type * from ...`.
*/
typeStarReexports: Record<Path, NotEmptyArray<TypeStarReexport>> | undefined;
/**
* Exports.
*/
/**
* `export default ...`.
*/
defaultExport: DefaultExport | undefined;
/**
* `export {...}`.
*/
namedExports: NotEmptyArray<NamedExport> | undefined;
/**
* `export (class|const|function|var...) ...`.
*/
declarationExports: Record<Name, DeclarationExport> | undefined;
/**
* `export type {...}`.
*/
typeNamedExports: NotEmptyArray<TypeNamedExport> | undefined;
/**
* `export type ...`.
*/
typeExports: Record<Name, TypeExport> | undefined;
/**
* `export interface ...`.
*/
interfaceExports: Record<Name, NotEmptyArray<InterfaceExport>> | undefined;
/**
* `export namespace ...`.
*/
namespaceExports: Record<Name, NotEmptyArray<NamespaceExport>> | undefined;
/**
* `module.exports = ...`.
*/
commonJsNamespaceExport: CommonJsNamespaceExport | undefined;
/**
* `(module.)exports.foo = ...`.
*/
commonJsExports: Record<Name, CommonJsExport> | undefined;
/**
* Syntax errors of module.
*/
errors: Record<LineColumn, string> | undefined;
};
/**
* Readonly array with at least one element.
*/
type NotEmptyArray<Element> = readonly [Element, ...Element[]];
/**
* Parsed JSON presentation of `export type ...` statement.
*/
type TypeExport = Position & {
isDeclare?: true;
};
/**
* Parsed JSON presentation of `import type {...} from ...` statement.
*/
type TypeNamedImport = Position & {
default?: Name;
names?: Names;
};
/**
* Parsed JSON presentation of `import type * as ...` statement.
*/
type TypeNamespaceImport = Position & {
namespace: Name;
};
/**
* Parsed JSON presentation of `export type * as ... from ...` statement.
*/
type TypeNamespaceReexport = Position & {
namespace: Name;
};
/**
* Parsed JSON presentation of `export type * from ...` statement.
*/
type TypeStarReexport = Position;

View file

@ -0,0 +1,174 @@
'use strict';
exports.createParseFunction = undefined;
exports.addError = undefined;
exports.CONTEXT_KEY = undefined;
exports.getCacheKey = undefined;
exports.getPosition = undefined;
exports.removeErrorsCausedByOverloading = undefined;
exports.spacesRegExp = undefined;
exports.stripComments = undefined;
/**
* Adds some parse error to parse results.
*/
const addError = exports.addError = (importsExports, message, start, end) => {
const { source } = importsExports[CONTEXT_KEY];
var { errors } = importsExports;
errors !== null && errors !== void 0 ? errors : (errors = importsExports.errors = { __proto__: null });
const additionalOffset = end !== undefined && end < start + 2 ? 100 : 0;
const fullMessage = end === undefined
? message
: `${message}:\n${source.slice(start, Math.min(end + additionalOffset, start + 200))}`;
const lineColumn = getLineColumnByIndex(importsExports, start);
const currentError = errors[lineColumn];
errors[lineColumn] = currentError === undefined ? fullMessage : `${currentError}\n${fullMessage}`;
};
/**
* Internal key for `Context` in `MutableImportsExports`.
*/
const CONTEXT_KEY = exports.CONTEXT_KEY = Symbol.for('parse-imports-exports:context-key');
{
const { createParseFunction } = require('parse-statements');
Object.assign(exports, {createParseFunction});
};
/**
* Get key for cache of parse functions by options.
*/
const getCacheKey = exports.getCacheKey = (options) => {
if (options === undefined) {
return '';
}
var cacheKey = '';
if (options.ignoreCommonJsExports === true) {
cacheKey += 'ignoreCommonJsExports';
}
if (options.ignoreDynamicImports === true) {
cacheKey += 'ignoreDynamicImports';
}
if (options.ignoreRegexpLiterals === true) {
cacheKey += 'ignoreRegexpLiterals';
}
if (options.ignoreRequires === true) {
cacheKey += 'ignoreRequires';
}
if (options.ignoreStringLiterals === true) {
cacheKey += 'ignoreStringLiterals';
}
return cacheKey;
};
/**
* Get position object by start and end indexes.
*/
const getPosition = exports.getPosition = (importsExports, start, end) => {
var _a;
return ((_a = importsExports[CONTEXT_KEY].options) === null || _a === void 0 ? void 0 : _a.includeLineColumn)
? {
start,
startLineColumn: getLineColumnByIndex(importsExports, start),
end,
endLineColumn: getLineColumnByIndex(importsExports, end),
}
: { start, end };
};
/**
* Removes errors, caused by function overloading.
* Re-declarations when overloading functions are not an error, so we remove them.
*/
const removeErrorsCausedByOverloading = exports.removeErrorsCausedByOverloading = (importsExports) => {
const { errors } = importsExports;
if (errors === undefined) {
return;
}
var previousError;
var previousLineColumn;
for (const lineColumn of Object.keys(errors)) {
const error = errors[lineColumn];
if ((error.startsWith('Duplicate exported declaration `function') ||
error.startsWith('Duplicate exported declaration `async function') ||
error.startsWith('Duplicate exported declaration `declare function')) &&
error.split(':')[0] === (previousError === null || previousError === void 0 ? void 0 : previousError.split(':')[0])) {
delete errors[previousLineColumn];
delete errors[lineColumn];
}
previousError = error;
previousLineColumn = lineColumn;
}
if (Object.keys(errors).length === 0) {
importsExports.errors = undefined;
}
};
/**
* Regexp that find all spaces.
*/
const spacesRegExp = exports.spacesRegExp = /\s+/g;
/**
* Strips comments from string interval from source.
*/
const stripComments = exports.stripComments = (source, intervalStart, intervalEnd, comments) => {
if (comments === undefined) {
return source.slice(intervalStart, intervalEnd);
}
var currentStart = intervalStart;
const parts = [];
for (const [{ start }, { end }] of comments) {
parts.push(source.slice(currentStart, start));
currentStart = end;
}
parts.push(source.slice(currentStart, intervalEnd));
return parts.join('');
};
/**
* Get `LineColumn` string by index in source.
*/
const getLineColumnByIndex = ({ [CONTEXT_KEY]: context }, index) => {
var { lineColumnCache, linesIndexes } = context;
lineColumnCache !== null && lineColumnCache !== void 0 ? lineColumnCache : (lineColumnCache = context.lineColumnCache = { __proto__: null });
var lineColumn = lineColumnCache[index];
if (lineColumn !== undefined) {
return lineColumn;
}
linesIndexes !== null && linesIndexes !== void 0 ? linesIndexes : (linesIndexes = context.linesIndexes = getLinesIndexes(context.source));
const numberOfLine = getNumberOfLine(index, linesIndexes);
const line = numberOfLine + 1;
const column = index - linesIndexes[numberOfLine] + 1;
lineColumn = `${line}:${column}`;
lineColumnCache[index] = lineColumn;
return lineColumn;
};
/**
* Get number of line where in which the character with the specified index is located.
*/
const getNumberOfLine = (index, linesIndexes) => {
const { length } = linesIndexes;
if (index >= linesIndexes[length - 1]) {
return length - 1;
}
var min = 0;
var max = length - 2;
while (min < max) {
var middle = min + ((max - min) >> 1);
if (index < linesIndexes[middle]) {
max = middle - 1;
}
else if (index >= linesIndexes[middle + 1]) {
min = middle + 1;
}
else {
min = middle;
break;
}
}
return min;
};
/**
* Get array of indexes of lines first symbols in source.
*/
const getLinesIndexes = (source) => {
var index = 0;
const lines = source.split('\n');
const indexes = new Array(lines.length);
for (var lineNumber = 0; lineNumber < lines.length; lineNumber += 1) {
indexes[lineNumber] = index;
index += lines[lineNumber].length + 1;
}
return indexes;
};

View file

@ -0,0 +1,162 @@
/**
* Adds some parse error to parse results.
*/
export const addError = (importsExports, message, start, end) => {
const { source } = importsExports[CONTEXT_KEY];
var { errors } = importsExports;
errors !== null && errors !== void 0 ? errors : (errors = importsExports.errors = { __proto__: null });
const additionalOffset = end !== undefined && end < start + 2 ? 100 : 0;
const fullMessage = end === undefined
? message
: `${message}:\n${source.slice(start, Math.min(end + additionalOffset, start + 200))}`;
const lineColumn = getLineColumnByIndex(importsExports, start);
const currentError = errors[lineColumn];
errors[lineColumn] = currentError === undefined ? fullMessage : `${currentError}\n${fullMessage}`;
};
/**
* Internal key for `Context` in `MutableImportsExports`.
*/
export const CONTEXT_KEY = Symbol.for('parse-imports-exports:context-key');
export { createParseFunction } from 'parse-statements';
/**
* Get key for cache of parse functions by options.
*/
export const getCacheKey = (options) => {
if (options === undefined) {
return '';
}
var cacheKey = '';
if (options.ignoreCommonJsExports === true) {
cacheKey += 'ignoreCommonJsExports';
}
if (options.ignoreDynamicImports === true) {
cacheKey += 'ignoreDynamicImports';
}
if (options.ignoreRegexpLiterals === true) {
cacheKey += 'ignoreRegexpLiterals';
}
if (options.ignoreRequires === true) {
cacheKey += 'ignoreRequires';
}
if (options.ignoreStringLiterals === true) {
cacheKey += 'ignoreStringLiterals';
}
return cacheKey;
};
/**
* Get position object by start and end indexes.
*/
export const getPosition = (importsExports, start, end) => {
var _a;
return ((_a = importsExports[CONTEXT_KEY].options) === null || _a === void 0 ? void 0 : _a.includeLineColumn)
? {
start,
startLineColumn: getLineColumnByIndex(importsExports, start),
end,
endLineColumn: getLineColumnByIndex(importsExports, end),
}
: { start, end };
};
/**
* Removes errors, caused by function overloading.
* Re-declarations when overloading functions are not an error, so we remove them.
*/
export const removeErrorsCausedByOverloading = (importsExports) => {
const { errors } = importsExports;
if (errors === undefined) {
return;
}
var previousError;
var previousLineColumn;
for (const lineColumn of Object.keys(errors)) {
const error = errors[lineColumn];
if ((error.startsWith('Duplicate exported declaration `function') ||
error.startsWith('Duplicate exported declaration `async function') ||
error.startsWith('Duplicate exported declaration `declare function')) &&
error.split(':')[0] === (previousError === null || previousError === void 0 ? void 0 : previousError.split(':')[0])) {
delete errors[previousLineColumn];
delete errors[lineColumn];
}
previousError = error;
previousLineColumn = lineColumn;
}
if (Object.keys(errors).length === 0) {
importsExports.errors = undefined;
}
};
/**
* Regexp that find all spaces.
*/
export const spacesRegExp = /\s+/g;
/**
* Strips comments from string interval from source.
*/
export const stripComments = (source, intervalStart, intervalEnd, comments) => {
if (comments === undefined) {
return source.slice(intervalStart, intervalEnd);
}
var currentStart = intervalStart;
const parts = [];
for (const [{ start }, { end }] of comments) {
parts.push(source.slice(currentStart, start));
currentStart = end;
}
parts.push(source.slice(currentStart, intervalEnd));
return parts.join('');
};
/**
* Get `LineColumn` string by index in source.
*/
const getLineColumnByIndex = ({ [CONTEXT_KEY]: context }, index) => {
var { lineColumnCache, linesIndexes } = context;
lineColumnCache !== null && lineColumnCache !== void 0 ? lineColumnCache : (lineColumnCache = context.lineColumnCache = { __proto__: null });
var lineColumn = lineColumnCache[index];
if (lineColumn !== undefined) {
return lineColumn;
}
linesIndexes !== null && linesIndexes !== void 0 ? linesIndexes : (linesIndexes = context.linesIndexes = getLinesIndexes(context.source));
const numberOfLine = getNumberOfLine(index, linesIndexes);
const line = numberOfLine + 1;
const column = index - linesIndexes[numberOfLine] + 1;
lineColumn = `${line}:${column}`;
lineColumnCache[index] = lineColumn;
return lineColumn;
};
/**
* Get number of line where in which the character with the specified index is located.
*/
const getNumberOfLine = (index, linesIndexes) => {
const { length } = linesIndexes;
if (index >= linesIndexes[length - 1]) {
return length - 1;
}
var min = 0;
var max = length - 2;
while (min < max) {
var middle = min + ((max - min) >> 1);
if (index < linesIndexes[middle]) {
max = middle - 1;
}
else if (index >= linesIndexes[middle + 1]) {
min = middle + 1;
}
else {
min = middle;
break;
}
}
return min;
};
/**
* Get array of indexes of lines first symbols in source.
*/
const getLinesIndexes = (source) => {
var index = 0;
const lines = source.split('\n');
const indexes = new Array(lines.length);
for (var lineNumber = 0; lineNumber < lines.length; lineNumber += 1) {
indexes[lineNumber] = index;
index += lines[lineNumber].length + 1;
}
return indexes;
};