Skip to content
Merged
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
44 changes: 35 additions & 9 deletions blueprints/ember-cli-typescript/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,10 @@ module.exports = {
let inRepoAddons = (this.project.pkg['ember-addon'] || {}).paths || [];
let hasMirage = 'ember-cli-mirage' in (this.project.pkg.devDependencies || {});
let isAddon = this.project.isEmberCLIAddon();
let includes = ['app', isAddon && 'addon', 'tests', 'types'].concat(inRepoAddons).filter(Boolean);
let isMU = this._detectMU();
Copy link
Collaborator

@NullVoxPopuli NullVoxPopuli Apr 27, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does javascript support unicode for variables? isμ? :trollface:

I actually just looked up upper case mu/μ, and it's pretty boring. (capital M). lame.

let includes = isMU ? ['src'] : ['app', isAddon && 'addon'].filter(Boolean);

includes = includes.concat(['tests', 'types']).concat(inRepoAddons);

// Mirage is already covered for addons because it's under `tests/`
if (hasMirage && !isAddon) {
Expand All @@ -51,15 +54,24 @@ module.exports = {
paths[`${appName}/mirage/*`] = [`${isAddon ? 'tests/dummy/' : ''}mirage/*`];
}

if (isAddon) {
paths[`${appName}/*`] = ['tests/dummy/app/*', 'app/*'];
if (isMU) {
if (isAddon) {
paths[`${appName}/src/*`] = ['tests/dummy/src/*'];
paths[`${dasherizedName}/src/*`] = ['src/*'];
} else {
paths[`${appName}/src/*`] = ['src/*'];
}
} else {
paths[`${appName}/*`] = ['app/*'];
}

if (isAddon) {
paths[dasherizedName] = ['addon'];
paths[`${dasherizedName}/*`] = ['addon/*'];
if (isAddon) {
paths[`${appName}/*`] = ['tests/dummy/app/*', 'app/*'];
} else {
paths[`${appName}/*`] = ['app/*'];
}

if (isAddon) {
paths[dasherizedName] = ['addon'];
paths[`${dasherizedName}/*`] = ['addon/*'];
}
}

for (let addon of inRepoAddons) {
Expand All @@ -79,11 +91,21 @@ module.exports = {
},

fileMapTokens(/*options*/) {
let isMU = this._detectMU();

// Return custom tokens to be replaced in your files.
return {
__app_name__(options) {
return options.inAddon ? 'dummy' : options.dasherizedModuleName;
},

__config_root__(options) {
if (isMU) {
return options.inAddon ? 'tests/dummy' : '.';
} else {
return options.inAddon ? 'tests/dummy/app' : 'app';
}
}
};
},

Expand Down Expand Up @@ -138,6 +160,10 @@ module.exports = {
return files;
},

_detectMU() {
return this.project.isModuleUnification && this.project.isModuleUnification();
},

_installPrecompilationHooks() {
let pkgPath = `${this.project.root}/package.json`;
let pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
Expand Down
2 changes: 1 addition & 1 deletion index.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ module.exports = {
shouldIncludeChildAddon(addon) {
// For testing, we have dummy in-repo addons set up, but e-c-ts doesn't depend on them;
// its dummy app does. Otherwise we'd have a circular dependency.
return addon.name !== 'in-repo-a' && addon.name !== 'in-repo-b';
return !['in-repo-a', 'in-repo-b', 'in-repo-c'].includes(addon.name);
},

setupPreprocessorRegistry(type, registry) {
Expand Down
12 changes: 10 additions & 2 deletions lib/commands/precompile.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,12 @@ module.exports = Command.extend({
let compiled = declSource.replace(/\.d\.ts$/, '.js');
this._copyFile(output, `${outDir}/${compiled}`, compiled);

// We can only do anything meaningful with declarations for files in addon/
// We can only do anything meaningful with declarations for files in addon/ or src/
if (this._isAddonFile(declSource)) {
let declDest = declSource.replace(/^addon\//, '');
this._copyFile(output, `${outDir}/${declSource}`, declDest);
} else if (this._isSrcFile(declSource)) {
this._copyFile(output, `${outDir}/${declSource}`, declSource);
}
}
}
Expand All @@ -60,7 +62,9 @@ module.exports = Command.extend({
},

_shouldCopy(source) {
return this._isAppFile(source) || this._isAddonFile(source);
return this._isAppFile(source)
|| this._isAddonFile(source)
|| this._isSrcFile(source);
},

_isAppFile(source) {
Expand All @@ -71,6 +75,10 @@ module.exports = Command.extend({
return source.indexOf('addon') === 0;
},

_isSrcFile(source) {
return source.indexOf('src') === 0;
},

_copyFile(output, source, dest) {
let segments = dest.split(/\/|\\/);

Expand Down
36 changes: 30 additions & 6 deletions lib/incremental-typescript-compiler/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,22 @@ module.exports = class IncrementalTypescriptCompiler {
}

treeForHost() {
let appTree = new TypescriptOutput(this, {
[`${this._relativeAppRoot()}/app`]: 'app',
});
let appRoot = `${this._relativeAppRoot()}/app`;
let srcRoot = `${this._relativeAppRoot()}/src`;

let trees = {};
if (fs.existsSync(appRoot)) {
trees[appRoot] = 'app';
}

if (fs.existsSync(srcRoot)) {
// MU apps currently include tests in production builds, and it's not yet clear
// how those will be filtered out in the future. We may or may not wind up needing
// to do that filtering here.
trees[srcRoot] = 'app/src';
}

let appTree = new TypescriptOutput(this, trees);
let mirage = this._mirageDirectory();
let mirageTree = mirage && new TypescriptOutput(this, {
[mirage]: 'app/mirage',
Expand All @@ -63,7 +75,16 @@ module.exports = class IncrementalTypescriptCompiler {
treeForAddons() {
let paths = {};
for (let addon of this.addons) {
paths[`${this._relativeAddonRoot(addon)}/addon`] = addon.name;
let absoluteRoot = this._addonRoot(addon);
let relativeRoot = this._relativeAddonRoot(addon);

if (fs.existsSync(`${absoluteRoot}/addon`)) {
paths[`${relativeRoot}/addon`] = addon.name;
}

if (fs.existsSync(`${absoluteRoot}/src`)) {
paths[`${relativeRoot}/src`] = `${addon.name}/src`;
}
}
return new TypescriptOutput(this, paths);
}
Expand Down Expand Up @@ -187,16 +208,19 @@ module.exports = class IncrementalTypescriptCompiler {
}
}

_relativeAddonRoot(addon) {
_addonRoot(addon) {
let addonRoot = addon.root;
if (addonRoot.indexOf(this.project.root) !== 0) {
let packagePath = resolve.sync(`${addon.pkg.name}/package.json`, {
basedir: this.project.root,
});
addonRoot = path.dirname(packagePath);
}
return addonRoot;
}

return addonRoot.replace(this.project.root, '');
_relativeAddonRoot(addon) {
return this._addonRoot(addon).replace(this.project.root, '');
}
};

103 changes: 103 additions & 0 deletions node-tests/blueprints/ember-cli-typescript-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const path = require('path');
const helpers = require('ember-cli-blueprint-test-helpers/helpers');
const chaiHelpers = require('ember-cli-blueprint-test-helpers/chai');
const Blueprint = require('ember-cli/lib/models/blueprint');
const Project = require('ember-cli/lib/models/project');

const ects = require('../../blueprints/ember-cli-typescript');

Expand Down Expand Up @@ -130,6 +131,108 @@ describe('Acceptance: ember-cli-typescript generator', function() {
});
});

describe('module unification', function() {
const originalIsMU = Project.prototype.isModuleUnification;

beforeEach(function() {
Project.prototype.isModuleUnification = () => true;
});

afterEach(function() {
Project.prototype.isModuleUnification = originalIsMU;
});

it('basic app', function() {
const args = ['ember-cli-typescript'];

return helpers
.emberNew()
.then(() => helpers.emberGenerate(args))
.then(() => {
const pkg = file('package.json');
expect(pkg).to.exist;

const pkgJson = JSON.parse(pkg.content);
expect(pkgJson.scripts.prepublishOnly).to.be.undefined;
expect(pkgJson.scripts.postpublish).to.be.undefined;
expect(pkgJson.devDependencies).to.include.all.keys('ember-data');
expect(pkgJson.devDependencies).to.include.all.keys('@types/ember-data');
expect(pkgJson.devDependencies).to.include.all.keys('ember-cli-qunit');
expect(pkgJson.devDependencies).to.include.all.keys('@types/ember-qunit', '@types/qunit');
expect(pkgJson.devDependencies).to.not.have.any.keys('@types/ember-mocha', '@types/mocha');

const tsconfig = file('tsconfig.json');
expect(tsconfig).to.exist;

const tsconfigJson = JSON.parse(tsconfig.content);
expect(tsconfigJson.compilerOptions.paths).to.deep.equal({
'my-app/tests/*': ['tests/*'],
'my-app/src/*': ['src/*'],
'*': ['types/*'],
});

expect(tsconfigJson.compilerOptions.inlineSourceMap).to.equal(true);
expect(tsconfigJson.compilerOptions.inlineSources).to.equal(true);

expect(tsconfigJson.include).to.deep.equal(['src/**/*', 'tests/**/*', 'types/**/*']);

const projectTypes = file('types/my-app/index.d.ts');
expect(projectTypes).to.exist;
expect(projectTypes).to.include(ects.APP_DECLARATIONS);

const environmentTypes = file('config/environment.d.ts');
expect(environmentTypes).to.exist;

const emberDataCatchallTypes = file('types/ember-data.d.ts');
expect(emberDataCatchallTypes).to.exist;
});
});

it('basic addon', function() {
const args = ['ember-cli-typescript'];

return helpers
.emberNew({ target: 'addon' })
.then(() => helpers.emberGenerate(args))
.then(() => {
const pkg = file('package.json');
expect(pkg).to.exist;

const pkgJson = JSON.parse(pkg.content);
expect(pkgJson.scripts.prepublishOnly).to.equal('ember ts:precompile');
expect(pkgJson.scripts.postpublish).to.equal('ember ts:clean');
expect(pkgJson.devDependencies).to.not.have.any.keys('ember-data');
expect(pkgJson.devDependencies).to.not.have.any.keys('@types/ember-data');
expect(pkgJson.devDependencies).to.include.all.keys('ember-cli-qunit');
expect(pkgJson.devDependencies).to.include.all.keys('@types/ember-qunit', '@types/qunit');
expect(pkgJson.devDependencies).to.not.have.any.keys('@types/ember-mocha', '@types/mocha');

const tsconfig = file('tsconfig.json');
expect(tsconfig).to.exist;

const tsconfigJson = JSON.parse(tsconfig.content);
expect(tsconfigJson.compilerOptions.paths).to.deep.equal({
'dummy/tests/*': ['tests/*'],
'dummy/src/*': ['tests/dummy/src/*'],
'my-addon/src/*': ['src/*'],
'*': ['types/*'],
});

expect(tsconfigJson.include).to.deep.equal(['src/**/*', 'tests/**/*', 'types/**/*']);

const projectTypes = file('types/dummy/index.d.ts');
expect(projectTypes).to.exist;
expect(projectTypes).not.to.include(ects.APP_DECLARATIONS);

const environmentTypes = file('tests/dummy/config/environment.d.ts');
expect(environmentTypes).to.exist;

const emberDataCatchallTypes = file('types/ember-data.d.ts');
expect(emberDataCatchallTypes).not.to.exist;
});
});
});

it('in-repo addons', function() {
const args = ['ember-cli-typescript'];

Expand Down
22 changes: 22 additions & 0 deletions node-tests/commands/precompile-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,26 @@ describe('Acceptance: ts:precompile command', function() {
expect(transpiled.content.trim()).to.equal(`export const testString = 'hello';`);
});
});

describe('module unification', function() {
it('generates .js and .d.ts files from the src tree', function() {
fs.ensureDirSync('src');
fs.writeFileSync('src/test-file.ts', `export const testString: string = 'hello';`);

let tsconfig = fs.readJSONSync('tsconfig.json');
tsconfig.include.push('src');
fs.writeJSONSync('tsconfig.json', tsconfig);

return ember(['ts:precompile'])
.then(() => {
let declaration = file('src/test-file.d.ts');
expect(declaration).to.exist;
expect(declaration.content.trim()).to.equal(`export declare const testString: string;`);

let transpiled = file('src/test-file.js');
expect(transpiled).to.exist;
expect(transpiled.content.trim()).to.equal(`export const testString = 'hello';`);
});
});
});
});
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,8 @@
],
"paths": [
"tests/dummy/lib/in-repo-a",
"tests/dummy/lib/in-repo-b"
"tests/dummy/lib/in-repo-b",
"tests/dummy/lib/in-repo-c"
]
},
"prettier": {
Expand Down
10 changes: 10 additions & 0 deletions tests/dummy/lib/in-repo-c/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/* eslint-env node */
'use strict';

module.exports = {
name: 'in-repo-c',

isDevelopingAddon() {
return true;
}
};
12 changes: 12 additions & 0 deletions tests/dummy/lib/in-repo-c/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"name": "in-repo-c",
"keywords": [
"ember-addon"
],
"dependencies": {
"ember-cli-babel": "*"
},
"devDependencies": {
"ember-cli-typescript": "*"
}
}
4 changes: 4 additions & 0 deletions tests/dummy/lib/in-repo-c/src/test-file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// This should wind up in the addon tree
const value: string = 'in-repo-c/src/test-file';

export default value;
4 changes: 4 additions & 0 deletions tests/dummy/src/test-file.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// This should wind up in the app's src tree
const value: string = 'dummy/src/test-file';

export default value;
Empty file.
Loading