Skip to content

Commit

Permalink
Merge pull request #573 from sveltejs/typescript
Browse files Browse the repository at this point in the history
[WIP] Convert to TypeScript
  • Loading branch information
Rich-Harris authored May 22, 2017
2 parents 2194de9 + 5cbe6b7 commit 84a1571
Show file tree
Hide file tree
Showing 129 changed files with 1,339 additions and 862 deletions.
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,11 @@
"rollup-plugin-commonjs": "^7.0.0",
"rollup-plugin-json": "^2.1.0",
"rollup-plugin-node-resolve": "^2.0.0",
"rollup-plugin-typescript": "^0.8.1",
"rollup-watch": "^3.2.2",
"source-map": "^0.5.6",
"source-map-support": "^0.4.8"
"source-map-support": "^0.4.8",
"typescript": "^2.3.2"
},
"nyc": {
"include": [
Expand Down
9 changes: 9 additions & 0 deletions rename.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
const glob = require( 'glob' );
const fs = require( 'fs' );

glob.sync( 'src/**/*.js' ).forEach( file => {
console.log( file );
const js = fs.readFileSync( file, 'utf-8' );
fs.writeFileSync( file.replace( /\.js$/, '.ts' ), js );
fs.unlinkSync( file );
});
23 changes: 17 additions & 6 deletions rollup/rollup.config.main.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,35 @@
import path from 'path';
import nodeResolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import json from 'rollup-plugin-json';
import buble from 'rollup-plugin-buble';
import typescript from 'rollup-plugin-typescript';

const src = path.resolve( 'src' );

export default {
entry: 'src/index.js',
entry: 'src/index.ts',
moduleName: 'svelte',
targets: [
{ dest: 'compiler/svelte.js', format: 'umd' }
],
plugins: [
{
resolveId ( importee, importer ) {
// bit of a hack — TypeScript only really works if it can resolve imports,
// but they misguidedly chose to reject imports with file extensions. This
// means we need to resolve them here
if ( importer && importer.startsWith( src ) && importee[0] === '.' && path.extname( importee ) === '' ) {
return path.resolve( path.dirname( importer ), `${importee}.ts` );
}
}
},
nodeResolve({ jsnext: true, module: true }),
commonjs(),
json(),
buble({
typescript({
include: 'src/**',
exclude: 'src/shared/**',
target: {
node: 4
}
typescript: require( 'typescript' )
})
],
sourceMap: true
Expand Down
4 changes: 2 additions & 2 deletions rollup/rollup.config.ssr.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ export default {
}
})
],
external: [ path.resolve( 'src/index.js' ), 'fs', 'path' ],
external: [ path.resolve( 'src/index.ts' ), 'fs', 'path' ],
paths: {
[ path.resolve( 'src/index.js' ) ]: '../compiler/svelte.js'
[ path.resolve( 'src/index.ts' ) ]: '../compiler/svelte.js'
},
sourceMap: true
};
113 changes: 69 additions & 44 deletions src/generators/Generator.js → src/generators/Generator.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,45 @@
import MagicString, { Bundle } from 'magic-string';
import { walk } from 'estree-walker';
import isReference from '../utils/isReference.js';
import flattenReference from '../utils/flattenReference.js';
import globalWhitelist from '../utils/globalWhitelist.js';
import reservedNames from '../utils/reservedNames.js';
import namespaces from '../utils/namespaces.js';
import { removeNode, removeObjectKey } from '../utils/removeNode.js';
import getIntro from './shared/utils/getIntro.js';
import getOutro from './shared/utils/getOutro.js';
import processCss from './shared/processCss.js';
import annotateWithScopes from '../utils/annotateWithScopes.js';
import isReference from '../utils/isReference';
import flattenReference from '../utils/flattenReference';
import globalWhitelist from '../utils/globalWhitelist';
import reservedNames from '../utils/reservedNames';
import namespaces from '../utils/namespaces';
import { removeNode, removeObjectKey } from '../utils/removeNode';
import getIntro from './shared/utils/getIntro';
import getOutro from './shared/utils/getOutro';
import processCss from './shared/processCss';
import annotateWithScopes from '../utils/annotateWithScopes';
import DomBlock from './dom/Block';
import SsrBlock from './server-side-rendering/Block';
import { Node, Parsed, CompileOptions } from '../interfaces';

const test = typeof global !== 'undefined' && global.__svelte_test;

export default class Generator {
constructor ( parsed, source, name, options ) {
parsed: Parsed;
source: string;
name: string;
options: CompileOptions;

imports: Node[];
helpers: Set<string>;
components: Set<string>;
events: Set<string>;
transitions: Set<string>;
importedComponents: Map<string, string>;

bindingGroups: string[];
expectedProperties: Set<string>;
css: string;
cssId: string;
usesRefs: boolean;

importedNames: Set<string>;
aliases: Map<string, string>;
usedNames: Set<string>;

constructor ( parsed: Parsed, source: string, name: string, options: CompileOptions ) {
this.parsed = parsed;
this.source = source;
this.name = name;
Expand All @@ -39,33 +64,33 @@ export default class Generator {
this.usesRefs = false;

// allow compiler to deconflict user's `import { get } from 'whatever'` and
// Svelte's builtin `import { get, ... } from 'svelte/shared.js'`;
// Svelte's builtin `import { get, ... } from 'svelte/shared.ts'`;
this.importedNames = new Set();
this.aliases = new Map();
this._usedNames = new Set( [ name ] );
this.usedNames = new Set( [ name ] );
}

addSourcemapLocations ( node ) {
addSourcemapLocations ( node: Node ) {
walk( node, {
enter: node => {
enter: ( node: Node ) => {
this.code.addSourcemapLocation( node.start );
this.code.addSourcemapLocation( node.end );
}
});
}

alias ( name ) {
alias ( name: string ) {
if ( !this.aliases.has( name ) ) {
this.aliases.set( name, this.getUniqueName( name ) );
}

return this.aliases.get( name );
}

contextualise ( block, expression, context, isEventHandler ) {
contextualise ( block: DomBlock | SsrBlock, expression: Node, context: string, isEventHandler: boolean ) {
this.addSourcemapLocations( expression );

const usedContexts = [];
const usedContexts: string[] = [];

const { code, helpers } = this;
const { contexts, indexes } = block;
Expand All @@ -76,7 +101,7 @@ export default class Generator {
const self = this;

walk( expression, {
enter ( node, parent, key ) {
enter ( node: Node, parent: Node, key: string ) {
if ( /^Function/.test( node.type ) ) lexicalDepth += 1;

if ( node._scope ) {
Expand Down Expand Up @@ -138,7 +163,7 @@ export default class Generator {
}
},

leave ( node ) {
leave ( node: Node ) {
if ( /^Function/.test( node.type ) ) lexicalDepth -= 1;
if ( node._scope ) scope = scope.parent;
}
Expand All @@ -151,16 +176,16 @@ export default class Generator {
};
}

findDependencies ( contextDependencies, indexes, expression ) {
findDependencies ( contextDependencies: Map<string, string[]>, indexes: Map<string, string>, expression: Node ) {
if ( expression._dependencies ) return expression._dependencies;

let scope = annotateWithScopes( expression );
const dependencies = [];
const dependencies: string[] = [];

const generator = this; // can't use arrow functions, because of this.skip()

walk( expression, {
enter ( node, parent ) {
enter ( node: Node, parent: Node ) {
if ( node._scope ) {
scope = node._scope;
return;
Expand All @@ -180,7 +205,7 @@ export default class Generator {
}
},

leave ( node ) {
leave ( node: Node ) {
if ( node._scope ) scope = scope.parent;
}
});
Expand All @@ -196,22 +221,22 @@ export default class Generator {

generate ( result, options, { name, format } ) {
if ( this.imports.length ) {
const statements = [];
const statements: string[] = [];

this.imports.forEach( ( declaration, i ) => {
if ( format === 'es' ) {
statements.push( this.source.slice( declaration.start, declaration.end ) );
return;
}

const defaultImport = declaration.specifiers.find( x => x.type === 'ImportDefaultSpecifier' || x.type === 'ImportSpecifier' && x.imported.name === 'default' );
const namespaceImport = declaration.specifiers.find( x => x.type === 'ImportNamespaceSpecifier' );
const namedImports = declaration.specifiers.filter( x => x.type === 'ImportSpecifier' && x.imported.name !== 'default' );
const defaultImport = declaration.specifiers.find( ( x: Node ) => x.type === 'ImportDefaultSpecifier' || x.type === 'ImportSpecifier' && x.imported.name === 'default' );
const namespaceImport = declaration.specifiers.find( ( x: Node ) => x.type === 'ImportNamespaceSpecifier' );
const namedImports = declaration.specifiers.filter( ( x: Node ) => x.type === 'ImportSpecifier' && x.imported.name !== 'default' );

const name = ( defaultImport || namespaceImport ) ? ( defaultImport || namespaceImport ).local.name : `__import${i}`;
declaration.name = name; // hacky but makes life a bit easier later

namedImports.forEach( specifier => {
namedImports.forEach( ( specifier: Node ) => {
statements.push( `var ${specifier.local.name} = ${name}.${specifier.imported.name}` );
});

Expand All @@ -230,7 +255,7 @@ export default class Generator {

const compiled = new Bundle({ separator: '' });

function addString ( str ) {
function addString ( str: string ) {
compiled.addSource({
content: new MagicString( str )
});
Expand All @@ -250,7 +275,7 @@ export default class Generator {
});
}

parts.forEach( str => {
parts.forEach( ( str: string ) => {
const chunk = str.replace( pattern, '' );
if ( chunk ) addString( chunk );

Expand All @@ -274,11 +299,11 @@ export default class Generator {
};
}

getUniqueName ( name ) {
getUniqueName ( name: string ) {
if ( test ) name = `${name}$`;
let alias = name;
for ( let i = 1; reservedNames.has( alias ) || this.importedNames.has( alias ) || this._usedNames.has( alias ); alias = `${name}_${i++}` );
this._usedNames.add( alias );
for ( let i = 1; reservedNames.has( alias ) || this.importedNames.has( alias ) || this.usedNames.has( alias ); alias = `${name}_${i++}` );
this.usedNames.add( alias );
return alias;
}

Expand All @@ -287,13 +312,13 @@ export default class Generator {
return name => {
if ( test ) name = `${name}$`;
let alias = name;
for ( let i = 1; reservedNames.has( alias ) || this.importedNames.has( alias ) || this._usedNames.has( alias ) || localUsedNames.has( alias ); alias = `${name}_${i++}` );
for ( let i = 1; reservedNames.has( alias ) || this.importedNames.has( alias ) || this.usedNames.has( alias ) || localUsedNames.has( alias ); alias = `${name}_${i++}` );
localUsedNames.add( alias );
return alias;
};
}

parseJs ( ssr ) {
parseJs ( ssr: boolean = false ) {
const { source } = this;
const { js } = this.parsed;

Expand All @@ -315,23 +340,23 @@ export default class Generator {
removeNode( this.code, js.content, node );
imports.push( node );

node.specifiers.forEach( specifier => {
node.specifiers.forEach( ( specifier: Node ) => {
this.importedNames.add( specifier.local.name );
});
}
}

const defaultExport = body.find( node => node.type === 'ExportDefaultDeclaration' );
const defaultExport = body.find( ( node: Node ) => node.type === 'ExportDefaultDeclaration' );

if ( defaultExport ) {
defaultExport.declaration.properties.forEach( prop => {
defaultExport.declaration.properties.forEach( ( prop: Node ) => {
templateProperties[ prop.key.name ] = prop;
});
}

[ 'helpers', 'events', 'components', 'transitions' ].forEach( key => {
if ( templateProperties[ key ] ) {
templateProperties[ key ].value.properties.forEach( prop => {
templateProperties[ key ].value.properties.forEach( ( prop: node ) => {
this[ key ].add( prop.key.name );
});
}
Expand All @@ -340,11 +365,11 @@ export default class Generator {
if ( templateProperties.computed ) {
const dependencies = new Map();

templateProperties.computed.value.properties.forEach( prop => {
templateProperties.computed.value.properties.forEach( ( prop: Node ) => {
const key = prop.key.name;
const value = prop.value;

const deps = value.params.map( param => param.type === 'AssignmentPattern' ? param.left.name : param.name );
const deps = value.params.map( ( param: Node ) => param.type === 'AssignmentPattern' ? param.left.name : param.name );
dependencies.set( key, deps );
});

Expand All @@ -362,7 +387,7 @@ export default class Generator {
computations.push({ key, deps });
}

templateProperties.computed.value.properties.forEach( prop => visit( prop.key.name ) );
templateProperties.computed.value.properties.forEach( ( prop: Node ) => visit( prop.key.name ) );
}

if ( templateProperties.namespace ) {
Expand All @@ -374,7 +399,7 @@ export default class Generator {

if ( templateProperties.components ) {
let hasNonImportedComponent = false;
templateProperties.components.value.properties.forEach( property => {
templateProperties.components.value.properties.forEach( ( property: Node ) => {
const key = property.key.name;
const value = source.slice( property.value.start, property.value.end );
if ( this.importedNames.has( value ) ) {
Expand Down
Loading

0 comments on commit 84a1571

Please sign in to comment.