Skip to content
Permalink
Browse files

feat: Can parse some commonjs modules

  • Loading branch information...
iamthes committed Nov 17, 2017
1 parent f046f4f commit 1c8efe5a54cc2b40448188a46f2bc60f7329d6c2
Showing with 95 additions and 38 deletions.
  1. +3 −2 package.json
  2. +62 −0 src/commonjs.spec.ts
  3. +10 −5 src/entry.ts
  4. +9 −0 src/module.spec.ts
  5. +5 −1 src/module.ts
  6. +3 −25 src/parse.spec.ts
  7. +3 −5 src/parse.ts
@@ -1,6 +1,6 @@
{
"name": "esm-exports",
"version": "1.1.0-alpha.6",
"version": "1.1.0",
"description": "Parse ecmascript modules and collect names of export vars, functions, etc.",
"main": "lib",
"typings": "src/index.ts",
@@ -25,6 +25,7 @@
"clean": "rimraf lib",
"prerelease": "npm run clean",
"release": "standard-version",
"release:undo": "git reset --hard HEAD~ -- && node -p \"let {execSync: x} = require('child_process'); `${x(`git tag -d ${x(`git for-each-ref refs/tags --sort=-taggerdate --format=%(refname:short) --count=1`)}`)}`\"",
"prepublishOnly": "npm run build",
"setup": "npm i -g commitizen standard-version && commitizen init cz-conventional-changelog --save-dev",
"commit": "git-cz"
@@ -37,7 +38,7 @@
},
"devDependencies": {
"@types/mocha": "^2.2.44",
"@types/node": "^8.0.52",
"@types/node": "^8.0.53",
"@types/resolve": "0.0.4",
"chokidar-cli": "^1.2.0",
"concurrently": "^3.5.0",
@@ -0,0 +1,62 @@
import { parse } from './index';
import assert = require('assert');

it('assert with additional functions', () => {
const source = `
declare module "assert" {
function internal(value: any, message?: string): void;
namespace internal {
export function ifError(value: any): void;
}
export = internal;
}
`;
const result = parse(source);
assert(result.length === 2);
const [entry2, entry1] = result;

assert.equal(entry1.module, 'assert');
assert.equal(entry1.isDefault, false);
assert.equal(entry1.cjs, true);
assert.equal(entry1.ts, true);

assert.equal(entry2.name, 'ifError');
assert.equal(entry2.module, 'assert');
assert.equal(entry2.isDefault, false);
});

it.skip('commonjs module.exports 1', () => {
const source = `module.exports.myfunc = () => {}`;
const [entry] = parse(source);
assert(entry);
assert.equal(entry.name, 'myfunc');
assert.equal(entry.isDefault, false);
});

it.skip('commonjs module.exports 2', () => {
const source = `this.myfunc = 1`;
const [entry] = parse(source);
assert(entry);
assert.equal(entry.name, 'myfunc');
assert.equal(entry.isDefault, false);
});

it.skip('declare namespace', async () => {
const source = `
declare namespace through2 {
}
export = through2
`;
const [entry] = await parse(source);
assert.ifError(entry.module);
assert.equal(entry.isDefault, true);
assert.equal(entry.isNodejs, 'through2'); // import through2 = require('through2');
});

it.skip('export as namespace', () => {
const source = `
export = _;
export as namespace _;
`;
const result = parse(source);
});
@@ -4,6 +4,8 @@ type EntryConstructor = {
filepath?: string;
specifier?: string;
isDefault?: boolean;
cjs?: boolean;
ts?: boolean;
};

export class Entry {
@@ -23,21 +25,24 @@ export class Entry {
* Node module name.
*/
module?: string;

/**
* Flag indicates export default.
*/
isDefault: boolean;
cjs: boolean;
ts: boolean;

constructor({ name, filepath, specifier, module, isDefault }: EntryConstructor) {
constructor({ name, filepath, specifier, module, isDefault, cjs, ts }: EntryConstructor) {
this.name = name;
this.specifier = specifier;
this.isDefault = Boolean(isDefault);
this.module = module;
this.filepath = filepath;
this.cjs = cjs;
this.ts = ts;
}

hash() {
return `${this.name}/${this.module ? this.module : this.filepath}`;
}
// hash() {
// return `${this.name}/${this.module ? this.module : this.filepath}`;
// }
}
@@ -91,3 +91,12 @@ it('globals should be eliminated', async () => {
assert.equal(bastards.length, 0);
assert(result.filter(m => !m.name).length === 0, 'all entries must have name');
});

it('commonjs modules pkg-dir', async () => {
const result = await parse('pkg-dir', { basedir: rootPath });
assert(result.length > 0);
assert(result.filter(m => m.module !== 'pkg-dir').length === 0);
const [entry] = result;
assert(entry.module === 'pkg-dir');
assert(entry.cjs === true);
});
@@ -3,7 +3,7 @@ import { fileExtensions } from './file-extensions';
import { file } from './file';
import { Entry } from './entry';
import { AsyncOpts } from 'resolve';
import { dirname, resolve as resolvePath } from 'path';
import { dirname, extname, resolve as resolvePath } from 'path';
import { readdir, stat } from 'fs';

export const resolveOptions: AsyncOpts = {
@@ -38,6 +38,10 @@ export function module(name: string, options: ModuleOptions = {}): Promise<Entry
if (!resolved) {
return Promise.resolve(entries);
}
if (entries.length === 0 && extname(resolved) === '.js') {
// Looks like commonjs module.
return Promise.resolve([new Entry({ module: name, cjs: true })]);
}
let unnamed: Entry[];
[unnamed, entries] = entries.reduce((result: Entry[][], m) => {
result[Number(m.name != null)].push(m);
@@ -96,10 +96,10 @@ it('should extract declared module http', async () => {
}
export var out1: number = 1;
`;
let entries = await parse(source);
let out1 = entries.find(e => e.name === 'out1');
let result = await parse(source);
let out1 = result.find(e => e.name === 'out1');
assert(out1);
let httpEntries = entries.filter(e => e.module === 'http');
let httpEntries = result.filter(e => e.module === 'http');
assert.equal(httpEntries.length, 3);
assert.equal(httpEntries[0].name, 'METHODS');
assert.equal(httpEntries[1].name, 'c1');
@@ -121,25 +121,3 @@ it('should extract declared module events', async () => {
assert.equal(event.name, 'EventEmitter');
assert.equal(event.module, 'events');
});

// TODO: How it should behave?
it.skip('declare namespace', async () => {
let source = `
declare namespace through2 {
}
export = through2
`;
let [entry] = await parse(source);
assert.equal(entry.module, 'through2');
assert.equal(entry.isDefault, true);
});

it.skip('export as namespace', () => {
let source = `
export = _;
export as namespace _;
`;
const result = parse(source);
console.log("result", result);
});

@@ -23,7 +23,6 @@ export function parse(sourceText: string, options: ParseOptions = {}): Entry[] {
if (node.pos >= moduleEnd) {
module = undefined;
}
// (node.getText());
switch (node.kind) { // eslint-disable-line tslint/config
case ts.SyntaxKind.ModuleDeclaration: {
const isDeclare = Boolean(node.modifiers && node.modifiers.find(m => m.kind === ts.SyntaxKind.DeclareKeyword));
@@ -67,10 +66,9 @@ export function parse(sourceText: string, options: ParseOptions = {}): Entry[] {
result.push(new Entry({ name, module, filepath, isDefault }));
}
} break;
// case ts.SyntaxKind.ExportAssignment: {
// console.log("node", node);
// debugger;
// } break;
case ts.SyntaxKind.ExportAssignment: {
result.push(new Entry({ module, cjs: true, ts: true }));
} break;
}
ts.forEachChild(node, walk);
}

0 comments on commit 1c8efe5

Please sign in to comment.
You can’t perform that action at this time.