Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Correctly process ES6 inline/side-effect imports #79

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
/test/build
!/test/src/typings/*.d.ts
/test/src/**/*.d.ts
!/test/src/inline_imports/*.d.ts
/test/src/**/*.js
/test/src/**/*.js.map

Expand Down
68 changes: 44 additions & 24 deletions lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ const bomOptExp = /^\uFEFF?/;
const externalExp = /^([ \t]*declare module )(['"])(.+?)(\2[ \t]*{?.*)$/;
const importExp = /^([ \t]*(?:export )?(?:import .+? )= require\()(['"])(.+?)(\2\);.*)$/;
const importEs6Exp = /^([ \t]*(?:export|import) ?(?:(?:\* (?:as [^ ,]+)?)|.*)?,? ?(?:[^ ,]+ ?,?)(?:\{(?:[^ ,]+ ?,?)*\})? ?from )(['"])([^ ,]+)(\2;.*)$/;
const importEs6SideEffectExp = /^([ \t]*import )(['"])(.+?)(\2;.*)$/;
const importEs6InlineExp = /^(.*?import\()(['"])(.+?)(\2\).*)$/;
const referenceTagExp = /^[ \t]*\/\/\/[ \t]*<reference[ \t]+path=(["'])(.*?)\1?[ \t]*\/>.*$/;
const identifierExp = /^\w+(?:[\.-]\w+)*$/;
const fileExp = /^([\./].*|.:.*)$/;
Expand Down Expand Up @@ -183,7 +185,7 @@ export function bundle(options: Options): BundleResult {
mainFileContent += generatedLine + "\n";
});
mainFile = path.resolve(baseDir, "dts-bundle.tmp." + exportName + ".d.ts");
fs.writeFileSync(mainFile, mainFileContent, 'utf8');
fs.writeFileSync(mainFile, mainFileContent, { encoding: 'utf8' });
}

trace('\n### find typings ###');
Expand Down Expand Up @@ -424,7 +426,7 @@ export function bundle(options: Options): BundleResult {
}
}

fs.writeFileSync(outFile, content, 'utf8');
fs.writeFileSync(outFile, content, { encoding: 'utf8' });
bundleResult.emitted = true;
} else {
warning(" XXX Not emit due to exist files not found.")
Expand Down Expand Up @@ -587,7 +589,7 @@ export function bundle(options: Options): BundleResult {
if (fs.lstatSync(file).isDirectory()) { // if file is a directory then lets assume commonjs convention of an index file in the given folder
file = path.join(file, 'index.d.ts');
}
const code = fs.readFileSync(file, 'utf8').replace(bomOptExp, '').replace(/\s*$/, '');
const code = fs.readFileSync(file, { encoding: 'utf8' }).replace(bomOptExp, '').replace(/\s*$/, '');
res.indent = detectIndent(code) || indent;

// buffer multi-line comments, handle JSDoc
Expand Down Expand Up @@ -696,36 +698,51 @@ export function bundle(options: Options): BundleResult {

// import() statement or es6 import
if ((line.indexOf("from") >= 0 && (match = line.match(importEs6Exp))) ||
(line.indexOf("import") >=0 && (match = line.match(importEs6InlineExp))) ||
(line.indexOf("import") >=0 && (match = line.match(importEs6SideEffectExp))) ||
(line.indexOf("require") >= 0 && (match = line.match(importExp)))) {
const [_, lead, quote, moduleName, trail] = match;
assert(moduleName);

const impPath = path.resolve(path.dirname(file), moduleName);
let full = path.resolve(path.dirname(file), impPath); // combine with above line?

// filename (i.e. starts with a dot, slash or windows drive letter)
if (fileExp.test(moduleName)) {
// TODO: some module replacing is handled here, whereas the rest is
// done in the "rewrite global external modules" step. It may be
// more clear to do all of it in that step.
let modLine: ModLine = {
original: lead + quote + getExpName(impPath) + trail
};
res.lines.push(modLine);
let assumeExists;

let full = path.resolve(path.dirname(file), impPath);
// If full is not an existing file, then let's assume the extension .d.ts
if(!fs.existsSync(full) || fs.existsSync(full + '.d.ts')) {
if (fs.existsSync(full + '.d.ts')) {
full += '.d.ts';
assumeExists = true;
} else {
assumeExists = fs.existsSync(full);
}
trace(' - import relative %s (%s)', moduleName, full);

pushUnique(res.relativeImports, full);
res.importLineRef.push(modLine);
if (!assumeExists) { // probably a resource typescript doesn't know how to handle
res.lines.push({ original: line }); // TOD: still transform the path?

} else {
// TODO: some module replacing is handled here, whereas the rest is
// done in the "rewrite global external modules" step. It may be
// more clear to do all of it in that step.
let modLine: ModLine = {
original: removeDeclares(
lead + quote + getExpName(impPath) + trail
)
};
res.lines.push(modLine);

trace(' - import relative %s (%s)', moduleName, full);

pushUnique(res.relativeImports, full);
res.importLineRef.push(modLine);
}
}
// identifier
else {
let modLine: ModLine = {
original: line
original: removeDeclares(line)
};
trace(' - import external %s', moduleName);

Expand All @@ -750,7 +767,7 @@ export function bundle(options: Options): BundleResult {
trace(' - declare %s', moduleName);
pushUnique(res.exports, moduleName);
let modLine: ModLine = {
original: line
original: inSourceTypings(file) ? removeDeclares(line) : line
};
res.relativeRef.push(modLine); // TODO
res.lines.push(modLine);
Expand All @@ -762,13 +779,10 @@ export function bundle(options: Options): BundleResult {
let [_, sp, static1, pub, static2, ident] = match;
line = sp + static1 + static2 + ident;
}
if (inSourceTypings(file)) {
// for internal typings, remove the 'declare' keyword (but leave 'export' intact)
res.lines.push({ original: line.replace(/^(export )?declare /g, '$1') });
}
else {
res.lines.push({ original: line });
}

res.lines.push({
original: inSourceTypings(file) ? removeDeclares(line) : line
});
}
});

Expand Down Expand Up @@ -825,6 +839,12 @@ function replaceImportExportEs6(line: string, replacer: (str: string) => string)
return line;
}

function removeDeclares(line: string) {
return line
.replace(/^(export )?declare /g, '$1')
.replace(/^\s*declare (const|let|module)/g, '$1')
}

function replaceExternal(line: string, replacer: (str: string) => string) {
let match = line.match(externalExp);
if (match) {
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dts-bundle",
"version": "0.7.3",
"name": "@arshaw/dts-bundle",
"version": "0.7.3-fork",
"description": "Export TypeScript .d.ts files as an external module definition",
"keywords": [
"typescript",
Expand Down
13 changes: 13 additions & 0 deletions test/expected/es6/foo-mx.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ declare module 'foo-mx' {
import { A } from "foo-mx/lib/subC";
import { bar } from "foo-mx/lib/subD";
import { foo as buzz } from "foo-mx/lib/subE";
import "foo-mx/lib/subF";
export function indexA(): subB.A;
export function indexB(): subB.B;
export function indexC(): A;
Expand Down Expand Up @@ -60,3 +61,15 @@ declare module 'foo-mx/lib/subE' {
export function bar(): A;
}

declare module 'foo-mx/lib/subF' {
export interface A {
name: string;
}
export class B {
name: string;
}
export default function test(): A;
export function foo(): A;
export function bar(): A;
}

13 changes: 13 additions & 0 deletions test/expected/inline_imports/foo-mx.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

declare module 'foo-mx' {
const _default: import('foo-mx/lib').Plugin;
export default _default;
}

declare module 'foo-mx/lib' {
export interface Plugin {
name: string
action: any
}
}

1 change: 1 addition & 0 deletions test/src/es6/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as subB from "./sub";
import subC, {A} from "./lib/subC";
import {bar} from "./lib/subD";
import {foo as buzz} from "./lib/subE";
import "./lib/subF";

export function indexA() {
return subA();
Expand Down
11 changes: 11 additions & 0 deletions test/src/es6/lib/subF.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export interface A {
name: string;
}

export class B {
name: string;
}

export default function test(): A { return null; }
export function foo(): A { return null; }
export function bar(): A { return null; }
2 changes: 2 additions & 0 deletions test/src/inline_imports/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
declare const _default: import('./lib').Plugin;
export default _default;
4 changes: 4 additions & 0 deletions test/src/inline_imports/lib.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface Plugin {
name: string
action: any
}
46 changes: 46 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,50 @@ describe('dts bundle', function () {
assert.strictEqual(getFile(actualFile), getFile(expectedFile));
});

(function testit(name, assertion, run) {
var buildDir = path.resolve(__dirname, 'src', 'inline_imports');
var call = function (done) {
var testDir = path.join(tmpDir, name);
var expDir = path.join(expectDir, name);

mkdirp.sync(testDir);

ncp.ncp(buildDir, testDir, function (err) {
if (err) {
done(err);
return;
}
assertion(testDir, expDir);
done();
});
};

var label = 'bundle ' + name;

if (run === 'skip') {
it.skip(label, call);
}
else if (run === 'only') {
it.only(label, call);
}
else {
it(label, call);
}
})('inline_imports', function (actDir, expDir) {
var result = dts.bundle({
name: 'foo-mx',
main: path.join(actDir, 'index.d.ts'),
newline: '\n',
verbose: true,
headerPath: "none"
});
var name = 'foo-mx.d.ts';
var actualFile = path.join(actDir, name);
assert.isTrue(result.emitted, "not emit " + actualFile);
var expectedFile = path.join(expDir, name);
assert.strictEqual(getFile(actualFile), getFile(expectedFile));
});

(function testit(name, assertion, run) {
var buildDir = path.resolve(__dirname, 'build', 'es6');
var call = function (done) {
Expand Down Expand Up @@ -431,6 +475,7 @@ describe('dts bundle', function () {
'lib/subC.d.ts',
'lib/subD.d.ts',
'lib/subE.d.ts',
'lib/subF.d.ts',
'sub.d.ts'
]);
assert.strictEqual(getFile(actualFile), getFile(expectedFile));
Expand Down Expand Up @@ -478,6 +523,7 @@ describe('dts bundle', function () {
'lib/subC.d.ts',
'lib/subD.d.ts',
'lib/subE.d.ts',
'lib/subF.d.ts',
'sub.d.ts'
]);
assert.strictEqual(getFile(actualFile), getFile(expectedFile));
Expand Down