Skip to content

Add .gts support #5

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

Closed
wants to merge 1 commit into from
Closed
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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
},
"dependencies": {
"chalk": "^4.0.0",
"content-tag": "^2.0.2",
"remove-types": "^1.0.0"
},
"devDependencies": {
Expand Down
98 changes: 90 additions & 8 deletions src/typescript-blueprint-polyfill.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
const { removeTypes } = require('remove-types');
const chalk = require('chalk');
const { replaceExtension, isTypeScriptFile } = require('./utils');
const path = require('path');
const {
replaceExtension,
replaceTypeScriptExtension,
isTypeScriptFile,
} = require('./utils');

module.exports = function (context) {
const blueprintClass = context._super.constructor.prototype;
Expand Down Expand Up @@ -73,12 +77,12 @@ module.exports = function (context) {
context.convertToJS = async function (fileInfo) {
let rendered = await fileInfo.render();

const transformed = await removeTypes(rendered);

fileInfo.rendered = transformed;

fileInfo.displayPath = replaceExtension(fileInfo.displayPath, '.js');
fileInfo.outputPath = replaceExtension(fileInfo.outputPath, '.js');
fileInfo.rendered = await removeTypes(
path.extname(fileInfo.displayPath),
rendered
);
fileInfo.displayPath = replaceTypeScriptExtension(fileInfo.displayPath);
fileInfo.outputPath = replaceTypeScriptExtension(fileInfo.outputPath);

return fileInfo;
};
Expand Down Expand Up @@ -147,3 +151,81 @@ module.exports = function (context) {
}, []);
};
};

/**
Removes types from .ts and .gts files.
Based on the code in ember-cli: https://github.com/ember-cli/ember-cli/blob/2dc099a90dc0a4e583e43e4020d691b342f1e891/lib/models/blueprint.js#L531

@private
@method removeTypes
@param {string} extension
@param {string} code
@return {Promise}
*/
async function removeTypes(extension, code) {
const { removeTypes: removeTypesFn } = require('remove-types');

if (extension === '.gts') {
const { Preprocessor } = require('content-tag');
const preprocessor = new Preprocessor();
// Strip template tags
const templateTagIdentifier = (index) =>
`template = __TEMPLATE_TAG_${index}__;`;
const templateTagIdentifierBraces = (index) =>
`(template = __TEMPLATE_TAG_${index}__);`;
const templateTagMatches = preprocessor.parse(code);
let strippedCode = code;
for (let i = 0; i < templateTagMatches.length; i++) {
const match = templateTagMatches[i];
const templateTag = substringBytes(
code,
match.range.start,
match.range.end
);
strippedCode = strippedCode.replace(
templateTag,
templateTagIdentifier(i)
);
}

// Remove types
const transformed = await removeTypesFn(strippedCode);

// Readd stripped template tags
let transformedWithTemplateTag = transformed;
for (let i = 0; i < templateTagMatches.length; i++) {
const match = templateTagMatches[i];
const templateTag = substringBytes(
code,
match.range.start,
match.range.end
);
transformedWithTemplateTag = transformedWithTemplateTag.replace(
templateTagIdentifier(i),
templateTag
);
transformedWithTemplateTag = transformedWithTemplateTag.replace(
templateTagIdentifierBraces(i),
templateTag
);
}

return transformedWithTemplateTag;
}

return await removeTypesFn(code);
}

/**
* Takes a substring of a string based on byte offsets.
* @private
* @method substringBytes
* @param {string} value : The input string.
* @param {number} start : The byte index of the substring start.
* @param {number} end : The byte index of the substring end.
* @return {string} : The substring.
*/
function substringBytes(value, start, end) {
let buf = Buffer.from(value);
return buf.subarray(start, end).toString();
}
15 changes: 14 additions & 1 deletion src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,24 @@ function replaceExtension(filePath, newExt) {
});
}

function replaceTypeScriptExtension(filePath) {
const extensionMap = {
'.ts': '.js',
'.gts': '.gjs',
};
const ext = path.extname(filePath);
const newExt = extensionMap[ext];

return replaceExtension(filePath, newExt);
}

function isTypeScriptFile(filePath) {
return path.extname(filePath) === '.ts';
const extension = path.extname(filePath);
return extension === '.ts' || extension === '.gts';
}

module.exports = {
replaceExtension,
replaceTypeScriptExtension,
isTypeScriptFile,
};
58 changes: 56 additions & 2 deletions test/generate.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,28 @@ const JS_FIXTURE = `export default function foo(a, b) {
}
`;

const GTS_FIXTURE = `import Component from '@glimmer/component';

interface Signature {
Args: {
foo: string;
}
}

export default class Foo extends Component<Signature> {
bar: string = 'bar';
<template>{{@foo}} {{this.bar}}</template>
}
`;

const GJS_FIXTURE = `import Component from '@glimmer/component';

export default class Foo extends Component {
bar = 'bar';
<template>{{@foo}} {{this.bar}}</template>
}
`;

const ROOT = process.cwd();
const EmberCLITargets = ['ember-cli-3-24', 'ember-cli-3-28', 'ember-cli'];

Expand Down Expand Up @@ -86,6 +108,19 @@ describe('ember generate', () => {
return a + b;
}
`,
'__name__.gts': `import Component from '@glimmer/component';

interface Signature {
Args: {
foo: string;
}
}

export default class <%=classifiedModuleName %> extends Component<Signature> {
bar: string = 'bar';
<template>{{@foo}} {{this.bar}}</template>
}
`
},
},
},
Expand All @@ -98,16 +133,20 @@ describe('ember generate', () => {
await ember(['generate', 'my-blueprint', 'foo']);

const generated = await file('app/my-blueprints/foo.js');

expect(generated).toEqual(JS_FIXTURE);

const generatedGjs = await file('app/my-blueprints/foo.gjs');
expect(generatedGjs).toEqual(GJS_FIXTURE);
});

test('it generates typescript with --typescript', async () => {
await ember(['generate', 'my-blueprint', 'foo', '--typescript']);

const generated = await file('app/my-blueprints/foo.ts');

expect(generated).toEqual(TS_FIXTURE);

const generatedGts = await file('app/my-blueprints/foo.gts');
expect(generatedGts).toEqual(GTS_FIXTURE);
});

test('it generates typescript when isTypeScriptProject is true', async () => {
Expand All @@ -119,6 +158,9 @@ describe('ember generate', () => {

const generated = await file('app/my-blueprints/foo.ts');
expect(generated).toEqual(TS_FIXTURE);

const generatedGts = await file('app/my-blueprints/foo.gts');
expect(generatedGts).toEqual(GTS_FIXTURE);
});

test('it generates javascript when isTypeScriptProject is explicitly false', async () => {
Expand All @@ -130,6 +172,9 @@ describe('ember generate', () => {

const generated = await file('app/my-blueprints/foo.js');
expect(generated).toEqual(JS_FIXTURE);

const generatedGjs = await file('app/my-blueprints/foo.gjs');
expect(generatedGjs).toEqual(GJS_FIXTURE);
});

test('it generates typescript if {typescript: true} is present in ember-cli', async () => {
Expand All @@ -141,13 +186,19 @@ describe('ember generate', () => {

const generated = await file('app/my-blueprints/foo.ts');
expect(generated).toEqual(TS_FIXTURE);

const generatedGts = await file('app/my-blueprints/foo.gts');
expect(generatedGts).toEqual(GTS_FIXTURE);
});

test('does not generate typescript when --no-typescript is passed', async () => {
await ember(['generate', 'my-blueprint', 'foo', '--no-typescript']);

const generated = await file('app/my-blueprints/foo.js');
expect(generated).toEqual(JS_FIXTURE);

const generatedGjs = await file('app/my-blueprints/foo.gjs');
expect(generatedGjs).toEqual(GJS_FIXTURE);
});

test('does not generate typescript when --no-typescript is passed, even in a typescript project', async () => {
Expand All @@ -159,6 +210,9 @@ describe('ember generate', () => {

const generated = await file('app/my-blueprints/foo.js');
expect(generated).toEqual(JS_FIXTURE);

const generatedGjs = await file('app/my-blueprints/foo.gjs');
expect(generatedGjs).toEqual(GJS_FIXTURE);
});
});

Expand Down
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2445,6 +2445,11 @@ content-disposition@0.5.4:
dependencies:
safe-buffer "5.2.1"

content-tag@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/content-tag/-/content-tag-2.0.2.tgz#978802d97df21516daa10d78e2a1f148e89eab8b"
integrity sha512-qHRyTp02dgzRK2tsCFxZ1H289bZOuSLNpupr6prvnSFq4SFPmNlBKbbE5PCMb+8+Z1a1z+yCVtXvQIGUCCa3lQ==

content-type@~1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
Expand Down
Loading