Skip to content

Instantly share code, notes, and snippets.

@shuhei
Created May 12, 2016 17:32
Show Gist options
  • Save shuhei/7f80280733191d49c85a902b385af1ba to your computer and use it in GitHub Desktop.
Save shuhei/7f80280733191d49c85a902b385af1ba to your computer and use it in GitHub Desktop.
Angular 2 Offline Compile
// https://github.com/angular/angular/blob/master/modules/%40angular/compiler/test/offline_compiler_util.ts
// https://github.com/angular/angular/blob/master/modules/%40angular/compiler/test/offline_compiler_codegen_untyped.ts
/* eslint-env node */
import {OutputEmitter} from '@angular/compiler/src/output/abstract_emitter';
import {createOfflineCompileUrlResolver} from '@angular/compiler/src/url_resolver';
// import {JavaScriptEmitter} from '@angular/compiler/src/output/js_emitter';
import {JavaScriptEmitter} from './js_emitter';
import {
CompileDirectiveMetadata,
CompileTypeMetadata,
CompileTemplateMetadata
} from '@angular/compiler/src/compile_metadata';
import {ImportGenerator} from '@angular/compiler/src/output/path_util';
import {Parser} from '@angular/compiler/src/expression_parser/parser';
import {Lexer} from '@angular/compiler/src/expression_parser/lexer';
import {HtmlParser} from '@angular/compiler/src/html_parser';
import {TemplateParser} from '@angular/compiler/src/template_parser';
import {MockXHR} from '@angular/compiler/testing/xhr_mock';
import {
OfflineCompiler,
NormalizedComponentWithViewDirectives,
SourceModule
} from '@angular/compiler/src/offline_compiler';
import {StyleCompiler} from '@angular/compiler/src/style_compiler';
import {ViewCompiler} from '@angular/compiler/src/view_compiler/view_compiler';
import {DirectiveNormalizer} from '@angular/compiler/src/directive_normalizer';
import {MockSchemaRegistry} from '@angular/compiler/testing/schema_registry_mock';
import {Console} from '@angular/compiler/core_private';
import {CompilerConfig} from '@angular/compiler/src/config';
class SimpleJsImportGenerator {
getImportPath(moduleUrlStr, importedUrlStr) {
// var moduleAssetUrl = ImportGenerator.parseAssetUrl(moduleUrlStr);
var importedAssetUrl = ImportGenerator.parseAssetUrl(importedUrlStr);
if (!!importedAssetUrl) {
return `${importedAssetUrl.packageName}/${importedAssetUrl.modulePath}`;
} else {
return importedUrlStr;
}
}
}
function _createOfflineCompiler(xhr, emitter) {
const urlResolver = createOfflineCompileUrlResolver();
const htmlParser = new HtmlParser();
const normalizer = new DirectiveNormalizer(xhr, urlResolver, htmlParser);
const tmplParser = new TemplateParser(new Parser(new Lexer()), new MockSchemaRegistry({}, {}),
htmlParser, new Console(), []);
const styleCompiler = new StyleCompiler(urlResolver);
const viewCompiler = new ViewCompiler(new CompilerConfig(true, true, true));
return new OfflineCompiler(normalizer, tmplParser, styleCompiler, viewCompiler, emitter, xhr);
}
function compileComp(emitter, comp) {
const xhr = new MockXHR();
const compiler = _createOfflineCompiler(xhr, emitter);
const result = compiler.normalizeDirectiveMetadata(comp)
.then(normComp => {
return compiler.compileTemplates([new NormalizedComponentWithViewDirectives(normComp, [], [])])
.source;
});
// xhr.flush();
return result;
}
class CompA {
user: string;
}
const compAMetadata = CompileDirectiveMetadata.create({
isComponent: true,
selector: 'comp-a',
type: new CompileTypeMetadata(
{name: 'CompA', moduleUrl: 'asset:foo/bar', runtime: CompA, diDeps: []}),
template: new CompileTemplateMetadata({
template: 'Hello World {{user}}',
styles: ['.redStyle { color: red; }']
})
});
function emit() {
// const emitter = new JavaScriptEmitter(new SimpleJsImportGenerator());
const emitter = new JavaScriptEmitter(new SimpleJsImportGenerator());
return compileComp(emitter, compAMetadata);
}
emit()
.then(source => console.log(source));
var import0 = require('@angular/core/src/linker/debug_context');
var import1 = require('@angular/core/src/linker/view');
var import2 = require('@angular/core/src/linker/view_type');
var import3 = require('@angular/core/src/change_detection/change_detection');
var import4 = require('@angular/core/src/linker/view_utils');
var import5 = require('@angular/core/src/metadata/view');
var import6 = require('asset:foo/bar');
var import7 = require('@angular/core/src/linker/element');
var import8 = require('@angular/core/src/linker/component_factory');
var styles_CompA = ['.redStyle[_ngcontent-%COMP%] { color: red; }'];
var nodeDebugInfos_CompA0 = [new import0.StaticNodeDebugInfo([],null,{})];
var renderType_CompA = null;
function _View_CompA0(viewUtils,parentInjector,declarationEl) {
var self = this;
import1.DebugAppView.call(this, _View_CompA0,renderType_CompA,import2.ViewType.COMPONENT,viewUtils,parentInjector,declarationEl,import3.ChangeDetectionStrategy.CheckAlways,nodeDebugInfos_CompA0);
}
_View_CompA0.prototype = Object.create(import1.DebugAppView.prototype);
_View_CompA0.prototype.createInternal = function(rootSelector) {
var self = this;
var parentRenderNode = self.renderer.createViewRoot(self.declarationAppElement.nativeElement);
self._text_0 = self.renderer.createText(parentRenderNode,'',self.debug(0,0,0));
self._expr_0 = import3.uninitialized;
self.init([],[self._text_0],[],[]);
return null;
};
_View_CompA0.prototype.detectChangesInternal = function(throwOnChange) {
var self = this;
self.detectContentChildrenChanges(throwOnChange);
self.debug(0,0,0);
var currVal_0 = import4.interpolate(1,'Hello World ',self.context.user,'');
if (import4.checkBinding(throwOnChange,self._expr_0,currVal_0)) {
self.renderer.setText(self._text_0,currVal_0);
self._expr_0 = currVal_0;
}
self.detectViewChildrenChanges(throwOnChange);
};
function viewFactory_CompA0(viewUtils,parentInjector,declarationEl) {
if ((renderType_CompA === null)) { (renderType_CompA = viewUtils.createRenderComponentType('asset:foo/bar class CompA - inline template',0,import5.ViewEncapsulation.Emulated,styles_CompA)); }
return new _View_CompA0(viewUtils,parentInjector,declarationEl);
}
Object.defineProperty(exports, 'viewFactory_CompA0', { get: function() { return viewFactory_CompA0; }});
var styles_CompA_Host = [];
var nodeDebugInfos_CompA_Host0 = [new import0.StaticNodeDebugInfo([import6.CompA],import6.CompA,{})];
var renderType_CompA_Host = null;
function _View_CompA_Host0(viewUtils,parentInjector,declarationEl) {
var self = this;
import1.DebugAppView.call(this, _View_CompA_Host0,renderType_CompA_Host,import2.ViewType.HOST,viewUtils,parentInjector,declarationEl,import3.ChangeDetectionStrategy.CheckAlways,nodeDebugInfos_CompA_Host0);
}
_View_CompA_Host0.prototype = Object.create(import1.DebugAppView.prototype);
_View_CompA_Host0.prototype.createInternal = function(rootSelector) {
var self = this;
self._el_0 = self.selectOrCreateHostElement('comp-a',rootSelector,self.debug(0,0,0));
self._appEl_0 = new import7.AppElement(0,null,self,self._el_0);
var compView_0 = viewFactory_CompA0(self.viewUtils,self.injector(0),self._appEl_0);
self._CompA_0_4 = new import6.CompA();
self._appEl_0.initComponent(self._CompA_0_4,[],compView_0);
compView_0.create(self._CompA_0_4,self.projectableNodes,null);
self.init([].concat([self._el_0]),[self._el_0],[],[]);
return self._appEl_0;
};
_View_CompA_Host0.prototype.injectorGetInternal = function(token,requestNodeIndex,notFoundResult) {
var self = this;
if (((token === import6.CompA) && (0 === requestNodeIndex))) { return self._CompA_0_4; }
return notFoundResult;
};
function viewFactory_CompA_Host0(viewUtils,parentInjector,declarationEl) {
if ((renderType_CompA_Host === null)) { (renderType_CompA_Host = viewUtils.createRenderComponentType('',0,import5.ViewEncapsulation.Emulated,styles_CompA_Host)); }
return new _View_CompA_Host0(viewUtils,parentInjector,declarationEl);
}
var CompANgFactory = new import8.ComponentFactory('comp-a',viewFactory_CompA_Host0,import6.CompA);
Object.defineProperty(exports, 'CompANgFactory', { get: function() { return CompANgFactory; }});
import * as o from '@angular/compiler/src/output/output_ast';
import {
isPresent,
isBlank,
isString,
evalExpression,
RegExpWrapper,
StringWrapper
} from '@angular/compiler/src/facade/lang';
import {BaseException} from '@angular/core';
import {OutputEmitter, EmitterVisitorContext} from '@angular/compiler/src/output/abstract_emitter';
import {AbstractJsEmitterVisitor} from '@angular/compiler/src/output/abstract_js_emitter';
import {ImportGenerator} from '@angular/compiler/src/output/path_util';
export class JavaScriptEmitter implements OutputEmitter {
constructor(_importGenerator) {
this._importGenerator = _importGenerator;
}
emitStatements(moduleUrl: string, stmts: o.Statement[], exportedVars: string[]): string {
var converter = new JsEmitterVisitor(moduleUrl);
var ctx = EmitterVisitorContext.createRoot(exportedVars);
converter.visitAllStatements(stmts, ctx);
var srcParts = [];
converter.importsWithPrefixes.forEach((prefix, importedModuleUrl) => {
// Note: can't write the real word for import as it screws up system.js auto detection...
srcParts.push(
`var ${prefix} = req` +
`uire('${this._importGenerator.getImportPath(moduleUrl, importedModuleUrl)}');`);
});
srcParts.push(ctx.toSource());
return srcParts.join('\n');
}
}
class JsEmitterVisitor extends AbstractJsEmitterVisitor {
importsWithPrefixes = new Map();
constructor(_moduleUrl: string) {
super();
this._moduleUrl = _moduleUrl;
}
visitExternalExpr(ast: o.ExternalExpr, ctx: EmitterVisitorContext): any {
if (isBlank(ast.value.name)) {
throw new BaseException(`Internal error: unknown identifier ${ast.value}`);
}
if (isPresent(ast.value.moduleUrl) && ast.value.moduleUrl != this._moduleUrl) {
var prefix = this.importsWithPrefixes.get(ast.value.moduleUrl);
if (isBlank(prefix)) {
prefix = `import${this.importsWithPrefixes.size}`;
this.importsWithPrefixes.set(ast.value.moduleUrl, prefix);
}
ctx.print(`${prefix}.`);
}
ctx.print(ast.value.name);
return null;
}
visitDeclareVarStmt(stmt: o.DeclareVarStmt, ctx: EmitterVisitorContext): any {
super.visitDeclareVarStmt(stmt, ctx);
if (ctx.isExportedVar(stmt.name)) {
ctx.println(exportVar(stmt.name));
}
return null;
}
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, ctx: EmitterVisitorContext): any {
super.visitDeclareFunctionStmt(stmt, ctx);
if (ctx.isExportedVar(stmt.name)) {
ctx.println(exportVar(stmt.name));
}
return null;
}
visitDeclareClassStmt(stmt: o.ClassStmt, ctx: EmitterVisitorContext): any {
super.visitDeclareClassStmt(stmt, ctx);
if (ctx.isExportedVar(stmt.name)) {
ctx.println(exportVar(stmt.name));
}
return null;
}
}
function exportVar(varName: string): string {
return `Object.defineProperty(exports, '${varName}', { get: function() { return ${varName}; }});`;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment