├── .editorconfig ├── .gitignore ├── .npmignore ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── LICENSE ├── README.md ├── bower.json ├── build ├── args.js ├── babel-options.js ├── paths.js ├── tasks │ ├── build.js │ ├── clean.js │ ├── dev.js │ ├── doc.js │ ├── lint.js │ ├── prepare-release.js │ └── test.js └── typescript-options.js ├── circle.yml ├── config.js ├── dist ├── amd │ ├── aurelia-ssr-engine.d.ts │ ├── aurelia-ssr-engine.js │ ├── cleanup.d.ts │ ├── cleanup.js │ ├── interfaces.d.ts │ ├── interfaces.js │ ├── property-descriptor.d.ts │ ├── property-descriptor.js │ ├── reflect.d.ts │ ├── reflect.js │ └── transformers │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── preboot.d.ts │ │ ├── preboot.js │ │ ├── styles.d.ts │ │ ├── styles.js │ │ ├── template.d.ts │ │ ├── template.js │ │ ├── title.d.ts │ │ ├── title.js │ │ ├── utils.d.ts │ │ └── utils.js ├── commonjs │ ├── aurelia-ssr-engine.d.ts │ ├── aurelia-ssr-engine.js │ ├── cleanup.d.ts │ ├── cleanup.js │ ├── interfaces.d.ts │ ├── interfaces.js │ ├── property-descriptor.d.ts │ ├── property-descriptor.js │ ├── reflect.d.ts │ ├── reflect.js │ └── transformers │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── preboot.d.ts │ │ ├── preboot.js │ │ ├── styles.d.ts │ │ ├── styles.js │ │ ├── template.d.ts │ │ ├── template.js │ │ ├── title.d.ts │ │ ├── title.js │ │ ├── utils.d.ts │ │ └── utils.js ├── es2015 │ ├── aurelia-ssr-engine.d.ts │ ├── aurelia-ssr-engine.js │ ├── cleanup.d.ts │ ├── cleanup.js │ ├── interfaces.d.ts │ ├── interfaces.js │ ├── property-descriptor.d.ts │ ├── property-descriptor.js │ ├── reflect.d.ts │ ├── reflect.js │ └── transformers │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── preboot.d.ts │ │ ├── preboot.js │ │ ├── styles.d.ts │ │ ├── styles.js │ │ ├── template.d.ts │ │ ├── template.js │ │ ├── title.d.ts │ │ ├── title.js │ │ ├── utils.d.ts │ │ └── utils.js ├── native-modules │ ├── aurelia-ssr-engine.d.ts │ ├── aurelia-ssr-engine.js │ ├── cleanup.d.ts │ ├── cleanup.js │ ├── interfaces.d.ts │ ├── interfaces.js │ ├── property-descriptor.d.ts │ ├── property-descriptor.js │ ├── reflect.d.ts │ ├── reflect.js │ └── transformers │ │ ├── index.d.ts │ │ ├── index.js │ │ ├── preboot.d.ts │ │ ├── preboot.js │ │ ├── styles.d.ts │ │ ├── styles.js │ │ ├── template.d.ts │ │ ├── template.js │ │ ├── title.d.ts │ │ ├── title.js │ │ ├── utils.d.ts │ │ └── utils.js └── system │ ├── aurelia-ssr-engine.d.ts │ ├── aurelia-ssr-engine.js │ ├── cleanup.d.ts │ ├── cleanup.js │ ├── interfaces.d.ts │ ├── interfaces.js │ ├── property-descriptor.d.ts │ ├── property-descriptor.js │ ├── reflect.d.ts │ ├── reflect.js │ └── transformers │ ├── index.d.ts │ ├── index.js │ ├── preboot.d.ts │ ├── preboot.js │ ├── styles.d.ts │ ├── styles.js │ ├── template.d.ts │ ├── template.js │ ├── title.d.ts │ ├── title.js │ ├── utils.d.ts │ └── utils.js ├── doc ├── CHANGELOG.md ├── shape-defs.js └── shape-doc.js ├── gulpfile.js ├── karma.conf.js ├── package.json ├── src ├── aurelia-ssr-engine.ts ├── cleanup.ts ├── interfaces.ts ├── property-descriptor.ts ├── reflect.ts └── transformers │ ├── index.ts │ ├── preboot.ts │ ├── styles.ts │ ├── template.ts │ ├── title.ts │ └── utils.ts ├── test ├── ssr-configuration.spec.d.ts └── ssr-configuration.spec.js ├── tsconfig.build.json ├── tsconfig.doc.json ├── tsconfig.json ├── tslint.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | # 2 space indentation 12 | [**.*] 13 | indent_style = space 14 | indent_size = 2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | jspm_packages 3 | bower_components 4 | .idea 5 | .DS_STORE 6 | build/reports 7 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | jspm_packages 2 | bower_components 3 | .idea -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | We'd love for you to contribute and to make this project even better than it is today! If this interests you, please begin by reading [our contributing guidelines](https://github.com/DurandalProject/about/blob/master/CONTRIBUTING.md). The contributing document will provide you with all the information you need to get started. Once you have read that, you will need to also [sign our CLA](http://goo.gl/forms/dI8QDDSyKR) before we can accept a Pull Request from you. More information on the process is included in the [contributor's guide](https://github.com/DurandalProject/about/blob/master/CONTRIBUTING.md). 4 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 19 | **I'm submitting a bug report** 20 | **I'm submitting a feature request** 21 | 22 | * **Library Version:** 23 | major.minor.patch-pre 24 | 25 | 26 | **Please tell us about your environment:** 27 | * **Operating System:** 28 | OSX 10.x|Linux (distro)|Windows [7|8|8.1|10] 29 | 30 | * **Node Version:** 31 | 6.2.0 32 | 36 | 37 | * **NPM Version:** 38 | 3.8.9 39 | 43 | 44 | * **JSPM OR Webpack AND Version** 45 | JSPM 0.16.32 | webpack 2.1.0-beta.17 46 | 52 | 53 | * **Browser:** 54 | all | Chrome XX | Firefox XX | Edge XX | IE XX | Safari XX | Mobile Chrome XX | Android X.X Web Browser | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView 55 | 56 | * **Language:** 57 | all | TypeScript X.X | ESNext 58 | 59 | 60 | **Current behavior:** 61 | 62 | 63 | **Expected/desired behavior:** 64 | 71 | 72 | 73 | * **What is the expected behavior?** 74 | 75 | 76 | * **What is the motivation / use case for changing the behavior?** 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2010 - 2016 Blue Spire Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aurelia-ssr-engine 2 | 3 | [![npm Version](https://img.shields.io/npm/v/aurelia-framework.svg)](https://www.npmjs.com/package/aurelia-ssr-engine) 4 | [![ZenHub](https://raw.githubusercontent.com/ZenHubIO/support/master/zenhub-badge.png)](https://zenhub.io) 5 | [![Join the chat at https://gitter.im/aurelia/discuss](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/aurelia/discuss?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 6 | [![CircleCI](https://circleci.com/gh/aurelia/ssr-engine.svg?style=shield)](https://circleci.com/gh/aurelia/ssr-engine) 7 | 8 | This library is part of the [Aurelia](http://www.aurelia.io/) platform and contains the aurelia framework which brings together all the required core aurelia libraries into a ready-to-go application-building platform. 9 | 10 | > To keep up to date on [Aurelia](http://www.aurelia.io/), please visit and subscribe to [the official blog](http://blog.aurelia.io/) and [our email list](http://eepurl.com/ces50j). We also invite you to [follow us on twitter](https://twitter.com/aureliaeffect). If you have questions, please [join our community on Gitter](https://gitter.im/aurelia/discuss) or use [stack overflow](http://stackoverflow.com/search?q=aurelia). Documentation can be found [in our developer hub](http://aurelia.io/hub.html). If you would like to have deeper insight into our development process, please install the [ZenHub](https://zenhub.io) Chrome or Firefox Extension and visit any of our repository's boards. 11 | 12 | ## Documentation 13 | 14 | You can read the documentation for the aurelia framework [here](http://aurelia.io/docs.html). If you would like to help improve this documentation, the source for many of the docs can be found in the doc folder within this repository. Other docs, not related to the general framework, but directed at specific libraries, can be found in the doc folder of those libraries. 15 | 16 | ## Platform Support 17 | 18 | This library can be used on the **server** only. 19 | 20 | ## Reporting Issues 21 | 22 | Please refer to the [issue template](ISSUE_TEMPLATE.md). Accompany any bug report with a demo of the issue using a [runnable Gist](https://gist.run/?id=381fdb1a4b0865a4c25026187db865ce). 23 | 24 | ## Building 25 | 26 | ```shell 27 | npm run build 28 | ``` 29 | 30 | ## Tests 31 | 32 | ```shell 33 | npm run test 34 | ``` 35 | 36 | ## Developing 37 | 38 | Run the tests in watch mode: 39 | 40 | ```shell 41 | npm run develop 42 | ``` 43 | 44 | ## Publishing 45 | 46 | 1. Bump the version 47 | 48 | ```shell 49 | npm run bump-version [ | major | minor | patch] 50 | ``` 51 | 52 | 2. Prepare the release (run tests, run build, docs, release notes) 53 | 54 | ```shell 55 | npm run prepare-release 56 | ``` 57 | 58 | 3. Commit, tag, npm publish (not automated) 59 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aurelia-ssr-engine", 3 | "version": "0.1.0", 4 | "description": "The server-side rendering engine for Aurelia.", 5 | "keywords": [ 6 | "aurelia", 7 | "server", 8 | "spa" 9 | ], 10 | "homepage": "http://aurelia.io", 11 | "main": "dist/commonjs/aurelia-ssr-engine.js", 12 | "moduleType": "node", 13 | "license": "MIT", 14 | "authors": [ 15 | "Rob Eisenberg (http://robeisenberg.com/)" 16 | ], 17 | "repository": { 18 | "type": "git", 19 | "url": "http://github.com/aurelia/ssr-engine" 20 | }, 21 | "dependencies": { 22 | "aurelia-binding": "^1.0.0", 23 | "aurelia-dependency-injection": "^1.0.0", 24 | "aurelia-loader": "^1.0.0", 25 | "aurelia-logging": "^1.0.0", 26 | "aurelia-metadata": "^1.0.0", 27 | "aurelia-pal": "^1.0.0", 28 | "aurelia-path": "^1.0.0", 29 | "aurelia-task-queue": "^1.0.0", 30 | "aurelia-templating": "^1.0.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /build/args.js: -------------------------------------------------------------------------------- 1 | var yargs = require('yargs'); 2 | 3 | var argv = yargs.argv, 4 | validBumpTypes = "major|minor|patch|prerelease".split("|"), 5 | bump = (argv.bump || 'patch').toLowerCase(); 6 | 7 | if(validBumpTypes.indexOf(bump) === -1) { 8 | throw new Error('Unrecognized bump "' + bump + '".'); 9 | } 10 | 11 | module.exports = { 12 | bump: bump 13 | }; 14 | -------------------------------------------------------------------------------- /build/babel-options.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var paths = require('./paths'); 3 | 4 | exports.base = function() { 5 | var config = { 6 | filename: '', 7 | filenameRelative: '', 8 | sourceMap: true, 9 | sourceRoot: '', 10 | moduleRoot: path.resolve('src').replace(/\\/g, '/'), 11 | moduleIds: false, 12 | comments: false, 13 | compact: false, 14 | code: true, 15 | presets: [ 'es2015-loose', 'stage-1' ], 16 | plugins: [ 17 | 'syntax-flow', 18 | 'transform-decorators-legacy', 19 | ] 20 | }; 21 | if (!paths.useTypeScriptForDTS) { 22 | config.plugins.push( 23 | ['babel-dts-generator', { 24 | packageName: paths.packageName, 25 | typings: '', 26 | suppressModulePath: true, 27 | suppressComments: false, 28 | memberOutputFilter: /^_.*/, 29 | suppressAmbientDeclaration: true 30 | }] 31 | ); 32 | }; 33 | config.plugins.push('transform-flow-strip-types'); 34 | return config; 35 | } 36 | 37 | exports.commonjs = function() { 38 | var options = exports.base(); 39 | options.plugins.push('transform-es2015-modules-commonjs'); 40 | return options; 41 | }; 42 | 43 | exports.amd = function() { 44 | var options = exports.base(); 45 | options.plugins.push('transform-es2015-modules-amd'); 46 | return options; 47 | }; 48 | 49 | exports.system = function() { 50 | var options = exports.base(); 51 | options.plugins.push('transform-es2015-modules-systemjs'); 52 | return options; 53 | }; 54 | 55 | exports.es2015 = function() { 56 | var options = exports.base(); 57 | options.presets = ['stage-1'] 58 | return options; 59 | }; 60 | 61 | exports['native-modules'] = function() { 62 | var options = exports.base(); 63 | options.presets[0] = 'es2015-loose-native-modules'; 64 | return options; 65 | } 66 | -------------------------------------------------------------------------------- /build/paths.js: -------------------------------------------------------------------------------- 1 | var path = require('path'); 2 | var fs = require('fs'); 3 | 4 | // hide warning // 5 | var emitter = require('events'); 6 | emitter.defaultMaxListeners = 20; 7 | 8 | var appRoot = 'src/'; 9 | var pkg = JSON.parse(fs.readFileSync('./package.json', 'utf-8')); 10 | 11 | var paths = { 12 | root: appRoot, 13 | source: appRoot + '**/*.js', 14 | html: appRoot + '**/*.html', 15 | style: 'styles/**/*.css', 16 | output: 'dist/', 17 | doc:'./doc', 18 | unitTests: 'test/**/*.js', 19 | e2eSpecsSrc: 'test/e2e/src/*.js', 20 | e2eSpecsDist: 'test/e2e/dist/', 21 | packageName: pkg.name, 22 | ignore: [], 23 | useTypeScriptForDTS: false, 24 | importsToAdd: [], 25 | sort: false 26 | }; 27 | 28 | paths.files = [ 29 | 'aurelia.js', 30 | 'framework-configuration.js', 31 | 'index.js' 32 | ].map(function(file){ 33 | return paths.root + file; 34 | }); 35 | 36 | module.exports = paths; 37 | -------------------------------------------------------------------------------- /build/tasks/build.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var runSequence = require('run-sequence'); 3 | var to5 = require('gulp-babel'); 4 | var paths = require('../paths'); 5 | var compilerOptions = require('../babel-options'); 6 | var compilerTsOptions = require('../typescript-options'); 7 | var assign = Object.assign || require('object.assign'); 8 | var through2 = require('through2'); 9 | var concat = require('gulp-concat'); 10 | var insert = require('gulp-insert'); 11 | var rename = require('gulp-rename'); 12 | var tools = require('aurelia-tools'); 13 | var ts = require('gulp-typescript'); 14 | var gutil = require('gulp-util'); 15 | var gulpIgnore = require('gulp-ignore'); 16 | var merge = require('merge2'); 17 | var jsName = paths.packageName + '.js'; 18 | var compileToModules = ['es2015', 'commonjs', 'amd', 'system', 'native-modules']; 19 | 20 | function cleanGeneratedCode() { 21 | return through2.obj(function(file, enc, callback) { 22 | file.contents = new Buffer(tools.cleanGeneratedCode(file.contents.toString('utf8'))); 23 | this.push(file); 24 | return callback(); 25 | }); 26 | } 27 | 28 | gulp.task('build-index', function() { 29 | var importsToAdd = paths.importsToAdd.slice(); 30 | 31 | var src = gulp.src(paths.files); 32 | 33 | if (paths.sort) { 34 | src = src.pipe(tools.sortFiles()); 35 | } 36 | 37 | if (paths.ignore) { 38 | paths.ignore.forEach(function(filename){ 39 | src = src.pipe(gulpIgnore.exclude(filename)); 40 | }); 41 | } 42 | 43 | return src.pipe(through2.obj(function(file, enc, callback) { 44 | file.contents = new Buffer(tools.extractImports(file.contents.toString('utf8'), importsToAdd)); 45 | this.push(file); 46 | return callback(); 47 | })) 48 | .pipe(concat(jsName)) 49 | .pipe(insert.transform(function(contents) { 50 | return tools.createImportBlock(importsToAdd) + contents; 51 | })) 52 | .pipe(gulp.dest(paths.output)); 53 | }); 54 | 55 | function gulpFileFromString(filename, string) { 56 | var src = require('stream').Readable({ objectMode: true }); 57 | src._read = function() { 58 | this.push(new gutil.File({ cwd: paths.appRoot, base: paths.output, path: filename, contents: new Buffer(string) })) 59 | this.push(null) 60 | } 61 | return src; 62 | } 63 | 64 | function srcForBabel() { 65 | return merge( 66 | gulp.src(paths.output + jsName), 67 | gulpFileFromString(paths.output + 'index.js', "export * from './" + paths.packageName + "';") 68 | ); 69 | } 70 | 71 | function srcForTypeScript() { 72 | return gulp 73 | .src(paths.output + paths.packageName + '.js') 74 | .pipe(rename(function (path) { 75 | if (path.extname == '.js') { 76 | path.extname = '.ts'; 77 | } 78 | })); 79 | } 80 | 81 | compileToModules.forEach(function(moduleType){ 82 | gulp.task('build-babel-' + moduleType, function () { 83 | return srcForBabel() 84 | .pipe(to5(assign({}, compilerOptions[moduleType]()))) 85 | .pipe(cleanGeneratedCode()) 86 | .pipe(gulp.dest(paths.output + moduleType)); 87 | }); 88 | 89 | if (moduleType === 'native-modules') return; // typescript doesn't support the combination of: es5 + native modules 90 | 91 | gulp.task('build-ts-' + moduleType, function () { 92 | var tsProject = ts.createProject( 93 | compilerTsOptions({ module: moduleType, target: moduleType == 'es2015' ? 'es2015' : 'es5' }), ts.reporter.defaultReporter()); 94 | var tsResult = srcForTypeScript().pipe(ts(tsProject)); 95 | return tsResult.js 96 | .pipe(gulp.dest(paths.output + moduleType)); 97 | }); 98 | }); 99 | 100 | gulp.task('build-dts', function() { 101 | var tsProject = ts.createProject( 102 | compilerTsOptions({ removeComments: false, target: "es2015", module: "es2015" }), ts.reporter.defaultReporter()); 103 | var tsResult = srcForTypeScript().pipe(ts(tsProject)); 104 | return tsResult.dts 105 | .pipe(gulp.dest(paths.output)); 106 | }); 107 | 108 | gulp.task('build', function(callback) { 109 | return runSequence( 110 | 'clean', 111 | 'build-index', 112 | compileToModules 113 | .map(function(moduleType) { return 'build-babel-' + moduleType }) 114 | .concat(paths.useTypeScriptForDTS ? ['build-dts'] : []), 115 | callback 116 | ); 117 | }); 118 | 119 | gulp.task('build-ts', function(callback) { 120 | return runSequence( 121 | 'clean', 122 | 'build-index', 123 | 'build-babel-native-modules', 124 | compileToModules 125 | .filter(function(moduleType) { return moduleType !== 'native-modules' }) 126 | .map(function(moduleType) { return 'build-ts-' + moduleType }) 127 | .concat(paths.useTypeScriptForDTS ? ['build-dts'] : []), 128 | callback 129 | ); 130 | }); 131 | -------------------------------------------------------------------------------- /build/tasks/clean.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var paths = require('../paths'); 3 | var del = require('del'); 4 | var vinylPaths = require('vinyl-paths'); 5 | 6 | gulp.task('clean', function() { 7 | return gulp.src([paths.output]) 8 | .pipe(vinylPaths(del)); 9 | }); 10 | -------------------------------------------------------------------------------- /build/tasks/dev.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var tools = require('aurelia-tools'); 3 | 4 | gulp.task('update-own-deps', function(){ 5 | tools.updateOwnDependenciesFromLocalRepositories(); 6 | }); 7 | 8 | gulp.task('build-dev-env', function () { 9 | tools.buildDevEnv(); 10 | }); 11 | -------------------------------------------------------------------------------- /build/tasks/doc.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var paths = require('../paths'); 3 | var typedoc = require('gulp-typedoc'); 4 | var runSequence = require('run-sequence'); 5 | var through2 = require('through2'); 6 | 7 | gulp.task('doc-generate', function(){ 8 | return gulp.src([paths.output + paths.packageName + '.d.ts']) 9 | .pipe(typedoc({ 10 | target: 'es6', 11 | includeDeclarations: true, 12 | moduleResolution: 'node', 13 | json: paths.doc + '/api.json', 14 | name: paths.packageName + '-docs',  15 | mode: 'modules', 16 | excludeExternals: true, 17 | ignoreCompilerErrors: false, 18 | version: true 19 | })); 20 | }); 21 | 22 | gulp.task('doc-shape', function(){ 23 | return gulp.src([paths.doc + '/api.json']) 24 | .pipe(through2.obj(function(file, enc, callback) { 25 | var json = JSON.parse(file.contents.toString('utf8')).children[0]; 26 | 27 | json = { 28 | name: paths.packageName, 29 | children: json.children, 30 | groups: json.groups 31 | }; 32 | 33 | file.contents = new Buffer(JSON.stringify(json)); 34 | this.push(file); 35 | return callback(); 36 | })) 37 | .pipe(gulp.dest(paths.doc)); 38 | }); 39 | 40 | gulp.task('doc', function(callback){ 41 | return runSequence( 42 | 'doc-generate', 43 | 'doc-shape', 44 | callback 45 | ); 46 | }); 47 | -------------------------------------------------------------------------------- /build/tasks/lint.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var paths = require('../paths'); 3 | var eslint = require('gulp-eslint'); 4 | 5 | gulp.task('lint', function() { 6 | return gulp.src(paths.source) 7 | .pipe(eslint()) 8 | .pipe(eslint.format()) 9 | .pipe(eslint.failOnError()); 10 | }); 11 | -------------------------------------------------------------------------------- /build/tasks/prepare-release.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var runSequence = require('run-sequence'); 3 | var paths = require('../paths'); 4 | var fs = require('fs'); 5 | var bump = require('gulp-bump'); 6 | var args = require('../args'); 7 | var conventionalChangelog = require('gulp-conventional-changelog'); 8 | 9 | gulp.task('changelog', function () { 10 | return gulp.src(paths.doc + '/CHANGELOG.md', { 11 | buffer: false 12 | }).pipe(conventionalChangelog({ 13 | preset: 'angular' 14 | })) 15 | .pipe(gulp.dest(paths.doc)); 16 | }); 17 | 18 | gulp.task('bump-version', function(){ 19 | return gulp.src(['./package.json', './bower.json']) 20 | .pipe(bump({type:args.bump })) //major|minor|patch|prerelease 21 | .pipe(gulp.dest('./')); 22 | }); 23 | 24 | gulp.task('prepare-release', function(callback){ 25 | return runSequence( 26 | 'build', 27 | 'lint', 28 | 'bump-version', 29 | 'doc', 30 | 'changelog', 31 | callback 32 | ); 33 | }); 34 | -------------------------------------------------------------------------------- /build/tasks/test.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | var Karma = require('karma').Server; 3 | 4 | /** 5 | * Run test once and exit 6 | */ 7 | gulp.task('test', function (done) { 8 | new Karma({ 9 | configFile: __dirname + '/../../karma.conf.js', 10 | singleRun: true 11 | }, done).start(); 12 | }); 13 | 14 | /** 15 | * Watch for file changes and re-run tests on each change 16 | */ 17 | gulp.task('tdd', function (done) { 18 | new Karma({ 19 | configFile: __dirname + '/../../karma.conf.js' 20 | }, done).start(); 21 | }); 22 | 23 | /** 24 | * Run test once with code coverage and exit 25 | */ 26 | gulp.task('cover', function (done) { 27 | new Karma({ 28 | configFile: __dirname + '/../../karma.conf.js', 29 | singleRun: true, 30 | reporters: ['coverage'], 31 | preprocessors: { 32 | 'test/**/*.js': ['babel'], 33 | 'src/**/*.js': ['babel', 'coverage'] 34 | }, 35 | coverageReporter: { 36 | type: 'html', 37 | dir: 'build/reports/coverage' 38 | } 39 | }, done).start(); 40 | }); 41 | -------------------------------------------------------------------------------- /build/typescript-options.js: -------------------------------------------------------------------------------- 1 | var tsconfig = require('../tsconfig.json'); 2 | var assign = Object.assign || require('object.assign'); 3 | 4 | module.exports = function(override) { 5 | return assign(tsconfig.compilerOptions, { 6 | "target": override && override.target || "es5", 7 | "typescript": require('typescript') 8 | }, override || {}); 9 | } 10 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | ##### 2 | # Circle CI 3 | # 4 | # For running docker images on circle ci, see: https://circleci.com/docs/docker 5 | # For circle.yml explanation, see: https://circleci.com/docs/manually 6 | ##### 7 | 8 | machine: 9 | node: 10 | version: 4.2.6 11 | 12 | dependencies: 13 | pre: 14 | - npm install -g gulp 15 | - npm install -g jspm 16 | override: 17 | - npm install 18 | - jspm install 19 | 20 | test: 21 | override: 22 | - gulp build 23 | - gulp test 24 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | System.config({ 2 | defaultJSExtensions: true, 3 | transpiler: "babel", 4 | babelOptions: { 5 | "optional": [ 6 | "runtime", 7 | "optimisation.modules.system" 8 | ] 9 | }, 10 | paths: { 11 | "github:*": "jspm_packages/github/*", 12 | "aurelia-framework/*": "dist/*", 13 | "npm:*": "jspm_packages/npm/*" 14 | }, 15 | 16 | map: { 17 | "aurelia-binding": "npm:aurelia-binding@1.0.0", 18 | "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0", 19 | "aurelia-loader": "npm:aurelia-loader@1.0.0", 20 | "aurelia-logging": "npm:aurelia-logging@1.0.0", 21 | "aurelia-metadata": "npm:aurelia-metadata@1.0.0", 22 | "aurelia-pal": "npm:aurelia-pal@1.0.0", 23 | "aurelia-pal-browser": "npm:aurelia-pal-browser@1.0.0", 24 | "aurelia-path": "npm:aurelia-path@1.0.0", 25 | "aurelia-task-queue": "npm:aurelia-task-queue@1.0.0", 26 | "aurelia-templating": "npm:aurelia-templating@1.0.0", 27 | "babel": "npm:babel-core@5.8.38", 28 | "babel-runtime": "npm:babel-runtime@5.8.38", 29 | "core-js": "npm:core-js@2.4.1", 30 | "github:jspm/nodelibs-assert@0.1.0": { 31 | "assert": "npm:assert@1.4.1" 32 | }, 33 | "github:jspm/nodelibs-buffer@0.1.0": { 34 | "buffer": "npm:buffer@3.6.0" 35 | }, 36 | "github:jspm/nodelibs-path@0.1.0": { 37 | "path-browserify": "npm:path-browserify@0.0.0" 38 | }, 39 | "github:jspm/nodelibs-process@0.1.2": { 40 | "process": "npm:process@0.11.6" 41 | }, 42 | "github:jspm/nodelibs-util@0.1.0": { 43 | "util": "npm:util@0.10.3" 44 | }, 45 | "github:jspm/nodelibs-vm@0.1.0": { 46 | "vm-browserify": "npm:vm-browserify@0.0.4" 47 | }, 48 | "npm:assert@1.4.1": { 49 | "assert": "github:jspm/nodelibs-assert@0.1.0", 50 | "buffer": "github:jspm/nodelibs-buffer@0.1.0", 51 | "process": "github:jspm/nodelibs-process@0.1.2", 52 | "util": "npm:util@0.10.3" 53 | }, 54 | "npm:aurelia-binding@1.0.0": { 55 | "aurelia-logging": "npm:aurelia-logging@1.0.0", 56 | "aurelia-metadata": "npm:aurelia-metadata@1.0.0", 57 | "aurelia-pal": "npm:aurelia-pal@1.0.0", 58 | "aurelia-task-queue": "npm:aurelia-task-queue@1.0.0" 59 | }, 60 | "npm:aurelia-dependency-injection@1.0.0": { 61 | "aurelia-metadata": "npm:aurelia-metadata@1.0.0", 62 | "aurelia-pal": "npm:aurelia-pal@1.0.0" 63 | }, 64 | "npm:aurelia-loader@1.0.0": { 65 | "aurelia-metadata": "npm:aurelia-metadata@1.0.0", 66 | "aurelia-path": "npm:aurelia-path@1.0.0" 67 | }, 68 | "npm:aurelia-metadata@1.0.0": { 69 | "aurelia-pal": "npm:aurelia-pal@1.0.0" 70 | }, 71 | "npm:aurelia-pal-browser@1.0.0": { 72 | "aurelia-pal": "npm:aurelia-pal@1.0.0" 73 | }, 74 | "npm:aurelia-task-queue@1.0.0": { 75 | "aurelia-pal": "npm:aurelia-pal@1.0.0" 76 | }, 77 | "npm:aurelia-templating@1.0.0": { 78 | "aurelia-binding": "npm:aurelia-binding@1.0.0", 79 | "aurelia-dependency-injection": "npm:aurelia-dependency-injection@1.0.0", 80 | "aurelia-loader": "npm:aurelia-loader@1.0.0", 81 | "aurelia-logging": "npm:aurelia-logging@1.0.0", 82 | "aurelia-metadata": "npm:aurelia-metadata@1.0.0", 83 | "aurelia-pal": "npm:aurelia-pal@1.0.0", 84 | "aurelia-path": "npm:aurelia-path@1.0.0", 85 | "aurelia-task-queue": "npm:aurelia-task-queue@1.0.0" 86 | }, 87 | "npm:babel-runtime@5.8.38": { 88 | "process": "github:jspm/nodelibs-process@0.1.2" 89 | }, 90 | "npm:buffer@3.6.0": { 91 | "base64-js": "npm:base64-js@0.0.8", 92 | "child_process": "github:jspm/nodelibs-child_process@0.1.0", 93 | "fs": "github:jspm/nodelibs-fs@0.1.2", 94 | "ieee754": "npm:ieee754@1.1.6", 95 | "isarray": "npm:isarray@1.0.0", 96 | "process": "github:jspm/nodelibs-process@0.1.2" 97 | }, 98 | "npm:core-js@2.4.1": { 99 | "fs": "github:jspm/nodelibs-fs@0.1.2", 100 | "path": "github:jspm/nodelibs-path@0.1.0", 101 | "process": "github:jspm/nodelibs-process@0.1.2", 102 | "systemjs-json": "github:systemjs/plugin-json@0.1.2" 103 | }, 104 | "npm:inherits@2.0.1": { 105 | "util": "github:jspm/nodelibs-util@0.1.0" 106 | }, 107 | "npm:path-browserify@0.0.0": { 108 | "process": "github:jspm/nodelibs-process@0.1.2" 109 | }, 110 | "npm:process@0.11.6": { 111 | "assert": "github:jspm/nodelibs-assert@0.1.0", 112 | "fs": "github:jspm/nodelibs-fs@0.1.2", 113 | "vm": "github:jspm/nodelibs-vm@0.1.0" 114 | }, 115 | "npm:util@0.10.3": { 116 | "inherits": "npm:inherits@2.0.1", 117 | "process": "github:jspm/nodelibs-process@0.1.2" 118 | }, 119 | "npm:vm-browserify@0.0.4": { 120 | "indexof": "npm:indexof@0.0.1" 121 | } 122 | } 123 | }); 124 | -------------------------------------------------------------------------------- /dist/amd/aurelia-ssr-engine.d.ts: -------------------------------------------------------------------------------- 1 | import './reflect'; 2 | import './property-descriptor'; 3 | import { RenderOptions, AppInitializationOptions } from './interfaces'; 4 | declare function render(options: RenderOptions, initOptions: AppInitializationOptions): Promise; 5 | export { render, AppInitializationOptions, RenderOptions }; 6 | -------------------------------------------------------------------------------- /dist/amd/aurelia-ssr-engine.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports", "./transformers", "./cleanup", "./reflect", "./property-descriptor"], function (require, exports, transformers_1, cleanup_1) { 2 | "use strict"; 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | function render(options, initOptions) { 5 | if (!options.url) { 6 | throw new Error('url is required when calling render()'); 7 | } 8 | if (!options.template) { 9 | throw new Error('template is required when calling render()'); 10 | } 11 | if (!initOptions.main) { 12 | throw new Error('main property is required'); 13 | } 14 | // this allows you to see aurelia logging in cmd/terminal 15 | // logging a lot of messages isn't good for performance though 16 | // so we need to set the loglevel in the app to something other than debug 17 | console.debug = console.log; 18 | // we'll want new instances of aurelia-pal and aurelia-pal-nodejs 19 | // because aurelia-pal holds the reference to the DOM 20 | delete require.cache[require.resolve('aurelia-pal')]; 21 | delete require.cache[require.resolve('aurelia-pal-nodejs')]; 22 | return start(initOptions, options.url.toString()) 23 | .then(function (ctx) { 24 | var document = ctx.pal.DOM.global.document; 25 | setInputDefaultValues(document.body); 26 | var html = transformers_1.transform({ app: ctx.aurelia.host.outerHTML, document: document }, options); 27 | ctx.stop(); 28 | cleanup_1.cleanup(options); 29 | return html; 30 | }); 31 | } 32 | exports.render = render; 33 | // .value property does not map to @value attribute, .defaultValue does. 34 | // so we need to copy that value over if we want it to serialize into HTML 35 | // without this there isn't a value attribute on any of the input tags 36 | function setInputDefaultValues(body) { 37 | var inputTags = Array.prototype.slice.call(body.querySelectorAll('input')); 38 | for (var i = 0; i < inputTags.length; i++) { 39 | var input = inputTags[i]; 40 | if (input.value != null) { 41 | input.defaultValue = input.value; 42 | } 43 | } 44 | } 45 | function start(options, requestUrl) { 46 | var _a = options.main(), initialize = _a.initialize, start = _a.start; 47 | var PLATFORM = initialize().PLATFORM; 48 | // url of jsdom should be equal to the request url 49 | // this dictates what page aurelia loads on startup 50 | PLATFORM.jsdom.reconfigure({ url: requestUrl }); 51 | return start(); 52 | } 53 | }); 54 | -------------------------------------------------------------------------------- /dist/amd/cleanup.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions } from './interfaces'; 2 | export declare function cleanup(options: RenderOptions): void; 3 | -------------------------------------------------------------------------------- /dist/amd/cleanup.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports"], function (require, exports) { 2 | "use strict"; 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | // aurelia-binding array observer 5 | var pop = Array.prototype.pop; 6 | var push = Array.prototype.push; 7 | var reverse = Array.prototype.reverse; 8 | var shift = Array.prototype.shift; 9 | var sort = Array.prototype.sort; 10 | var splice = Array.prototype.splice; 11 | var unshift = Array.prototype.unshift; 12 | function cleanup(options) { 13 | // aurelia-binding's array observer overrides the prototype of Array 14 | // which causes the Array global to have references to the app 15 | // and nodejs won't gc the app because of this 16 | Array.prototype.pop = pop; 17 | Array.prototype.push = push; 18 | Array.prototype.reverse = reverse; 19 | Array.prototype.shift = shift; 20 | Array.prototype.sort = sort; 21 | Array.prototype.splice = splice; 22 | Array.prototype.unshift = unshift; 23 | // delete the server bundle from cache 24 | if (process.mainModule) { 25 | rdelete(process.mainModule, options.bundlePath); 26 | } 27 | } 28 | exports.cleanup = cleanup; 29 | /** 30 | * Recursively go over all node modules and delete a specific module 31 | * so it can be garbage collected 32 | * @param m 33 | * @param key 34 | */ 35 | function rdelete(m, key) { 36 | if (m.parent && m.parent.filename === require.resolve(key)) { 37 | delete m.parent; 38 | } 39 | for (var i = m.children.length - 1; i >= 0; i--) { 40 | if (m.children[i].filename === require.resolve(key)) { 41 | m.children.splice(i, 1); 42 | } 43 | else { 44 | rdelete(m.children[i], key); 45 | } 46 | } 47 | } 48 | ; 49 | }); 50 | -------------------------------------------------------------------------------- /dist/amd/interfaces.d.ts: -------------------------------------------------------------------------------- 1 | import { IDom } from 'aurelia-pal-nodejs/dist/dom'; 2 | import { Aurelia } from 'aurelia-framework'; 3 | export interface RenderOptions { 4 | /** 5 | * The path to the bundle 6 | */ 7 | bundlePath: string; 8 | /** 9 | * The requested url 10 | */ 11 | url?: URL; 12 | /** 13 | * The template where indicates where server side rendered html will be inserted 14 | */ 15 | template: string; 16 | /** 17 | * Whether or not to use preboot. Preboot allows you to record (and playback) events 18 | * that occur before the client-app is loaded (defaults to false) 19 | */ 20 | preboot: boolean; 21 | /** 22 | * When using preboot, how long is the delay between Aurelia start and before the view has loaded 23 | */ 24 | replayDelay?: number; 25 | /** 26 | * Options that are passed to preboot 27 | */ 28 | prebootOptions?: any; 29 | /** 30 | * The queryselector(s) of the approot(s). Used by preboot 31 | * e.g. ['body'] 32 | */ 33 | appRoots?: string[]; 34 | } 35 | export interface AppInitializationOptions { 36 | /** 37 | * an initialize, start and stop function for the engine to call 38 | * to start and stop the aurelia app 39 | */ 40 | main: () => { 41 | initialize: () => { 42 | PLATFORM: any; 43 | }; 44 | start: () => Promise<{ 45 | aurelia: Aurelia; 46 | pal: AureliaPal; 47 | palNodeJS: AureliaPalNodeJS; 48 | stop: () => void; 49 | }>; 50 | stop: () => Promise; 51 | }; 52 | /** 53 | * The module id of the server main file (e.g. 'main') 54 | */ 55 | serverMainId?: string; 56 | } 57 | export interface TransformerContext { 58 | /** 59 | * The body of the server side rendered app 60 | */ 61 | app: string; 62 | /** 63 | * The JSDOM document 64 | */ 65 | document: any; 66 | } 67 | export interface Dom extends IDom { 68 | global: Window; 69 | } 70 | export interface AureliaPal { 71 | DOM: Dom; 72 | global: Window; 73 | reset(): void; 74 | } 75 | export interface AureliaPalNodeJS { 76 | configure(): void; 77 | initialize(): void; 78 | reset(): void; 79 | } 80 | -------------------------------------------------------------------------------- /dist/amd/interfaces.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports"], function (require, exports) { 2 | "use strict"; 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | }); 5 | -------------------------------------------------------------------------------- /dist/amd/property-descriptor.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelia/ssr-engine/5394083a067136364d58650a78f9200b18033bd8/dist/amd/property-descriptor.d.ts -------------------------------------------------------------------------------- /dist/amd/property-descriptor.js: -------------------------------------------------------------------------------- 1 | // instead of letting aurelia-pal define Object.getPropertyDescriptor we'll do it here 2 | // so nodejs can garbage collect aurelia-pal 3 | Object.getPropertyDescriptor = function (subject, name) { 4 | var pd = Object.getOwnPropertyDescriptor(subject, name); 5 | var proto = Object.getPrototypeOf(subject); 6 | while (typeof pd === 'undefined' && proto !== null) { 7 | pd = Object.getOwnPropertyDescriptor(proto, name); 8 | proto = Object.getPrototypeOf(proto); 9 | } 10 | return pd; 11 | }; 12 | -------------------------------------------------------------------------------- /dist/amd/reflect.d.ts: -------------------------------------------------------------------------------- 1 | declare var FEATURE_NO_ES2015: any; 2 | declare var FEATURE_NO_ESNEXT: any; 3 | declare namespace Reflect { 4 | function getOwnMetadata(metadataKey: any, target: any, targetKey: any): any; 5 | function defineMetadata(metadataKey: any, metadataValue: any, target: any, targetKey: any): any; 6 | function metadata(metadataKey: any, metadataValue: any): any; 7 | } 8 | -------------------------------------------------------------------------------- /dist/amd/reflect.js: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | if (typeof FEATURE_NO_ES2015 === 'undefined') { 3 | var bind_1 = Function.prototype.bind; 4 | if (typeof Reflect.defineProperty !== 'function') { 5 | Reflect.defineProperty = function (target, propertyKey, descriptor) { 6 | if (typeof target === 'object' ? target === null : typeof target !== 'function') { 7 | throw new TypeError('Reflect.defineProperty called on non-object'); 8 | } 9 | try { 10 | Object.defineProperty(target, propertyKey, descriptor); 11 | return true; 12 | } 13 | catch (e) { 14 | return false; 15 | } 16 | }; 17 | } 18 | if (typeof Reflect.construct !== 'function') { 19 | Reflect.construct = function (Target, args) { 20 | if (args) { 21 | switch (args.length) { 22 | case 0: return new Target(); 23 | case 1: return new Target(args[0]); 24 | case 2: return new Target(args[0], args[1]); 25 | case 3: return new Target(args[0], args[1], args[2]); 26 | case 4: return new Target(args[0], args[1], args[2], args[3]); 27 | } 28 | } 29 | var a = [null]; 30 | a.push.apply(a, args); 31 | return new (bind_1.apply(Target, a)); 32 | }; 33 | } 34 | if (typeof Reflect.ownKeys !== 'function') { 35 | Reflect.ownKeys = function (o) { return (Object.getOwnPropertyNames(o).concat(Object.getOwnPropertySymbols(o))); }; 36 | } 37 | } // endif FEATURE_NO_ES2015 38 | if (typeof FEATURE_NO_ESNEXT === 'undefined') { 39 | var emptyMetadata_1 = Object.freeze({}); 40 | var metadataContainerKey_1 = '__metadata__'; 41 | if (typeof Reflect.getOwnMetadata !== 'function') { 42 | Reflect.getOwnMetadata = function (metadataKey, target, targetKey) { 43 | if (target.hasOwnProperty(metadataContainerKey_1)) { 44 | return (target[metadataContainerKey_1][targetKey] || emptyMetadata_1)[metadataKey]; 45 | } 46 | }; 47 | } 48 | if (typeof Reflect.defineMetadata !== 'function') { 49 | Reflect.defineMetadata = function (metadataKey, metadataValue, target, targetKey) { 50 | var metadataContainer = target.hasOwnProperty(metadataContainerKey_1) ? target[metadataContainerKey_1] : (target[metadataContainerKey_1] = {}); 51 | var targetContainer = metadataContainer[targetKey] || (metadataContainer[targetKey] = {}); 52 | targetContainer[metadataKey] = metadataValue; 53 | }; 54 | } 55 | if (typeof Reflect.metadata !== 'function') { 56 | Reflect.metadata = function (metadataKey, metadataValue) { 57 | return function (target, targetKey) { 58 | Reflect.defineMetadata(metadataKey, metadataValue, target, targetKey); 59 | }; 60 | }; 61 | } 62 | } // endif FEATURE_NO_ESNEXT 63 | -------------------------------------------------------------------------------- /dist/amd/transformers/index.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export declare function transform(transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/amd/transformers/index.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports"], function (require, exports) { 2 | "use strict"; 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | // tslint:disable:no-var-requires 5 | var transformers = [ 6 | require('./template').default, 7 | require('./title').default, 8 | require('./styles').default, 9 | require('./preboot').default 10 | ]; 11 | // tslint:enable:no-var-requires 12 | function transform(transformerCtx, options) { 13 | var html = options.template; 14 | for (var i = 0; i < transformers.length; i++) { 15 | html = transformers[i](html, transformerCtx, options); 16 | } 17 | return html; 18 | } 19 | exports.transform = transform; 20 | }); 21 | -------------------------------------------------------------------------------- /dist/amd/transformers/preboot.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/amd/transformers/preboot.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports", "preboot", "./utils"], function (require, exports, preboot, utils_1) { 2 | "use strict"; 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | function default_1(html, transformerCtx, options) { 5 | if (options.preboot) { 6 | if (options.replayDelay === undefined) { 7 | options.replayDelay = 10; 8 | } 9 | // preboot catches all events that happens before Aurelia gets loaded client-side 10 | // so that they can be replayed afterwards 11 | var prebootOptions = Object.assign({ 12 | appRoot: options.appRoots || ['body'], 13 | eventSelectors: [ 14 | // for recording changes in form elements 15 | { selector: 'input,textarea', events: ['keypress', 'keyup', 'keydown', 'input', 'change'] }, 16 | { selector: 'select,option', events: ['change'] }, 17 | // when user hits return button in an input box 18 | { selector: 'input', events: ['keyup'], preventDefault: true, keyCodes: [13], freeze: true }, 19 | // for tracking focus (no need to replay) 20 | { selector: 'input,textarea', events: ['focusin', 'focusout', 'mousedown', 'mouseup'], noReplay: true }, 21 | { selector: 'button[type="submit"]', events: ['submit'], preventDefault: false, freeze: true }, 22 | { selector: 'form', events: ['submit'], preventDefault: true, freeze: true }, 23 | // user clicks on a button 24 | { selector: 'button:not([type="submit"])', events: ['click'], preventDefault: true, freeze: true } 25 | ] 26 | }, options.prebootOptions); 27 | var inlinePrebootCode = preboot.getInlineCode(prebootOptions); 28 | html = utils_1.appendToHead(html, "\r\n\r\n"); 29 | // preboot_browser can replay events that were stored by the preboot code 30 | html = utils_1.appendToBody(html, "\r\n\n "); 31 | } 32 | return html; 33 | } 34 | exports.default = default_1; 35 | ; 36 | }); 37 | -------------------------------------------------------------------------------- /dist/amd/transformers/styles.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/amd/transformers/styles.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports", "./utils"], function (require, exports, utils_1) { 2 | "use strict"; 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | function default_1(html, transformerCtx, options) { 5 | var headStyleTags = Array.prototype.slice.call(transformerCtx.document.head.querySelectorAll('style')); 6 | // copy over any style tags 7 | for (var i = 0; i < headStyleTags.length; i++) { 8 | html = utils_1.appendToHead(html, headStyleTags[i].outerHTML); 9 | } 10 | return html; 11 | } 12 | exports.default = default_1; 13 | ; 14 | }); 15 | -------------------------------------------------------------------------------- /dist/amd/transformers/template.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/amd/transformers/template.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports"], function (require, exports) { 2 | "use strict"; 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | function default_1(html, transformerCtx, options) { 5 | return options.template.replace('', transformerCtx.app); 6 | } 7 | exports.default = default_1; 8 | ; 9 | }); 10 | -------------------------------------------------------------------------------- /dist/amd/transformers/title.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/amd/transformers/title.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports"], function (require, exports) { 2 | "use strict"; 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | function default_1(html, transformerCtx, options) { 5 | var title = transformerCtx.document.head.querySelector('title'); 6 | return html.replace('', title.innerHTML); 7 | } 8 | exports.default = default_1; 9 | ; 10 | }); 11 | -------------------------------------------------------------------------------- /dist/amd/transformers/utils.d.ts: -------------------------------------------------------------------------------- 1 | export declare function appendToBody(htmlString: string, toAppend: string): string; 2 | export declare function appendToHead(htmlString: string, toAppend: string): string; 3 | -------------------------------------------------------------------------------- /dist/amd/transformers/utils.js: -------------------------------------------------------------------------------- 1 | define(["require", "exports"], function (require, exports) { 2 | "use strict"; 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | function appendToBody(htmlString, toAppend) { 5 | return htmlString.replace('', toAppend + ""); 6 | } 7 | exports.appendToBody = appendToBody; 8 | function appendToHead(htmlString, toAppend) { 9 | return htmlString.replace('', toAppend + ""); 10 | } 11 | exports.appendToHead = appendToHead; 12 | }); 13 | -------------------------------------------------------------------------------- /dist/commonjs/aurelia-ssr-engine.d.ts: -------------------------------------------------------------------------------- 1 | import './reflect'; 2 | import './property-descriptor'; 3 | import { RenderOptions, AppInitializationOptions } from './interfaces'; 4 | declare function render(options: RenderOptions, initOptions: AppInitializationOptions): Promise; 5 | export { render, AppInitializationOptions, RenderOptions }; 6 | -------------------------------------------------------------------------------- /dist/commonjs/aurelia-ssr-engine.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | require("./reflect"); 4 | require("./property-descriptor"); 5 | var transformers_1 = require("./transformers"); 6 | var cleanup_1 = require("./cleanup"); 7 | function render(options, initOptions) { 8 | if (!options.url) { 9 | throw new Error('url is required when calling render()'); 10 | } 11 | if (!options.template) { 12 | throw new Error('template is required when calling render()'); 13 | } 14 | if (!initOptions.main) { 15 | throw new Error('main property is required'); 16 | } 17 | // this allows you to see aurelia logging in cmd/terminal 18 | // logging a lot of messages isn't good for performance though 19 | // so we need to set the loglevel in the app to something other than debug 20 | console.debug = console.log; 21 | // we'll want new instances of aurelia-pal and aurelia-pal-nodejs 22 | // because aurelia-pal holds the reference to the DOM 23 | delete require.cache[require.resolve('aurelia-pal')]; 24 | delete require.cache[require.resolve('aurelia-pal-nodejs')]; 25 | return start(initOptions, options.url.toString()) 26 | .then(function (ctx) { 27 | var document = ctx.pal.DOM.global.document; 28 | setInputDefaultValues(document.body); 29 | var html = transformers_1.transform({ app: ctx.aurelia.host.outerHTML, document: document }, options); 30 | ctx.stop(); 31 | cleanup_1.cleanup(options); 32 | return html; 33 | }); 34 | } 35 | exports.render = render; 36 | // .value property does not map to @value attribute, .defaultValue does. 37 | // so we need to copy that value over if we want it to serialize into HTML 38 | // without this there isn't a value attribute on any of the input tags 39 | function setInputDefaultValues(body) { 40 | var inputTags = Array.prototype.slice.call(body.querySelectorAll('input')); 41 | for (var i = 0; i < inputTags.length; i++) { 42 | var input = inputTags[i]; 43 | if (input.value != null) { 44 | input.defaultValue = input.value; 45 | } 46 | } 47 | } 48 | function start(options, requestUrl) { 49 | var _a = options.main(), initialize = _a.initialize, start = _a.start; 50 | var PLATFORM = initialize().PLATFORM; 51 | // url of jsdom should be equal to the request url 52 | // this dictates what page aurelia loads on startup 53 | PLATFORM.jsdom.reconfigure({ url: requestUrl }); 54 | return start(); 55 | } 56 | -------------------------------------------------------------------------------- /dist/commonjs/cleanup.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions } from './interfaces'; 2 | export declare function cleanup(options: RenderOptions): void; 3 | -------------------------------------------------------------------------------- /dist/commonjs/cleanup.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | // aurelia-binding array observer 4 | var pop = Array.prototype.pop; 5 | var push = Array.prototype.push; 6 | var reverse = Array.prototype.reverse; 7 | var shift = Array.prototype.shift; 8 | var sort = Array.prototype.sort; 9 | var splice = Array.prototype.splice; 10 | var unshift = Array.prototype.unshift; 11 | function cleanup(options) { 12 | // aurelia-binding's array observer overrides the prototype of Array 13 | // which causes the Array global to have references to the app 14 | // and nodejs won't gc the app because of this 15 | Array.prototype.pop = pop; 16 | Array.prototype.push = push; 17 | Array.prototype.reverse = reverse; 18 | Array.prototype.shift = shift; 19 | Array.prototype.sort = sort; 20 | Array.prototype.splice = splice; 21 | Array.prototype.unshift = unshift; 22 | // delete the server bundle from cache 23 | if (process.mainModule) { 24 | rdelete(process.mainModule, options.bundlePath); 25 | } 26 | } 27 | exports.cleanup = cleanup; 28 | /** 29 | * Recursively go over all node modules and delete a specific module 30 | * so it can be garbage collected 31 | * @param m 32 | * @param key 33 | */ 34 | function rdelete(m, key) { 35 | if (m.parent && m.parent.filename === require.resolve(key)) { 36 | delete m.parent; 37 | } 38 | for (var i = m.children.length - 1; i >= 0; i--) { 39 | if (m.children[i].filename === require.resolve(key)) { 40 | m.children.splice(i, 1); 41 | } 42 | else { 43 | rdelete(m.children[i], key); 44 | } 45 | } 46 | } 47 | ; 48 | -------------------------------------------------------------------------------- /dist/commonjs/interfaces.d.ts: -------------------------------------------------------------------------------- 1 | import { IDom } from 'aurelia-pal-nodejs/dist/dom'; 2 | import { Aurelia } from 'aurelia-framework'; 3 | export interface RenderOptions { 4 | /** 5 | * The path to the bundle 6 | */ 7 | bundlePath: string; 8 | /** 9 | * The requested url 10 | */ 11 | url?: URL; 12 | /** 13 | * The template where indicates where server side rendered html will be inserted 14 | */ 15 | template: string; 16 | /** 17 | * Whether or not to use preboot. Preboot allows you to record (and playback) events 18 | * that occur before the client-app is loaded (defaults to false) 19 | */ 20 | preboot: boolean; 21 | /** 22 | * When using preboot, how long is the delay between Aurelia start and before the view has loaded 23 | */ 24 | replayDelay?: number; 25 | /** 26 | * Options that are passed to preboot 27 | */ 28 | prebootOptions?: any; 29 | /** 30 | * The queryselector(s) of the approot(s). Used by preboot 31 | * e.g. ['body'] 32 | */ 33 | appRoots?: string[]; 34 | } 35 | export interface AppInitializationOptions { 36 | /** 37 | * an initialize, start and stop function for the engine to call 38 | * to start and stop the aurelia app 39 | */ 40 | main: () => { 41 | initialize: () => { 42 | PLATFORM: any; 43 | }; 44 | start: () => Promise<{ 45 | aurelia: Aurelia; 46 | pal: AureliaPal; 47 | palNodeJS: AureliaPalNodeJS; 48 | stop: () => void; 49 | }>; 50 | stop: () => Promise; 51 | }; 52 | /** 53 | * The module id of the server main file (e.g. 'main') 54 | */ 55 | serverMainId?: string; 56 | } 57 | export interface TransformerContext { 58 | /** 59 | * The body of the server side rendered app 60 | */ 61 | app: string; 62 | /** 63 | * The JSDOM document 64 | */ 65 | document: any; 66 | } 67 | export interface Dom extends IDom { 68 | global: Window; 69 | } 70 | export interface AureliaPal { 71 | DOM: Dom; 72 | global: Window; 73 | reset(): void; 74 | } 75 | export interface AureliaPalNodeJS { 76 | configure(): void; 77 | initialize(): void; 78 | reset(): void; 79 | } 80 | -------------------------------------------------------------------------------- /dist/commonjs/interfaces.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | -------------------------------------------------------------------------------- /dist/commonjs/property-descriptor.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelia/ssr-engine/5394083a067136364d58650a78f9200b18033bd8/dist/commonjs/property-descriptor.d.ts -------------------------------------------------------------------------------- /dist/commonjs/property-descriptor.js: -------------------------------------------------------------------------------- 1 | // instead of letting aurelia-pal define Object.getPropertyDescriptor we'll do it here 2 | // so nodejs can garbage collect aurelia-pal 3 | Object.getPropertyDescriptor = function (subject, name) { 4 | var pd = Object.getOwnPropertyDescriptor(subject, name); 5 | var proto = Object.getPrototypeOf(subject); 6 | while (typeof pd === 'undefined' && proto !== null) { 7 | pd = Object.getOwnPropertyDescriptor(proto, name); 8 | proto = Object.getPrototypeOf(proto); 9 | } 10 | return pd; 11 | }; 12 | -------------------------------------------------------------------------------- /dist/commonjs/reflect.d.ts: -------------------------------------------------------------------------------- 1 | declare var FEATURE_NO_ES2015: any; 2 | declare var FEATURE_NO_ESNEXT: any; 3 | declare namespace Reflect { 4 | function getOwnMetadata(metadataKey: any, target: any, targetKey: any): any; 5 | function defineMetadata(metadataKey: any, metadataValue: any, target: any, targetKey: any): any; 6 | function metadata(metadataKey: any, metadataValue: any): any; 7 | } 8 | -------------------------------------------------------------------------------- /dist/commonjs/reflect.js: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | if (typeof FEATURE_NO_ES2015 === 'undefined') { 3 | var bind_1 = Function.prototype.bind; 4 | if (typeof Reflect.defineProperty !== 'function') { 5 | Reflect.defineProperty = function (target, propertyKey, descriptor) { 6 | if (typeof target === 'object' ? target === null : typeof target !== 'function') { 7 | throw new TypeError('Reflect.defineProperty called on non-object'); 8 | } 9 | try { 10 | Object.defineProperty(target, propertyKey, descriptor); 11 | return true; 12 | } 13 | catch (e) { 14 | return false; 15 | } 16 | }; 17 | } 18 | if (typeof Reflect.construct !== 'function') { 19 | Reflect.construct = function (Target, args) { 20 | if (args) { 21 | switch (args.length) { 22 | case 0: return new Target(); 23 | case 1: return new Target(args[0]); 24 | case 2: return new Target(args[0], args[1]); 25 | case 3: return new Target(args[0], args[1], args[2]); 26 | case 4: return new Target(args[0], args[1], args[2], args[3]); 27 | } 28 | } 29 | var a = [null]; 30 | a.push.apply(a, args); 31 | return new (bind_1.apply(Target, a)); 32 | }; 33 | } 34 | if (typeof Reflect.ownKeys !== 'function') { 35 | Reflect.ownKeys = function (o) { return (Object.getOwnPropertyNames(o).concat(Object.getOwnPropertySymbols(o))); }; 36 | } 37 | } // endif FEATURE_NO_ES2015 38 | if (typeof FEATURE_NO_ESNEXT === 'undefined') { 39 | var emptyMetadata_1 = Object.freeze({}); 40 | var metadataContainerKey_1 = '__metadata__'; 41 | if (typeof Reflect.getOwnMetadata !== 'function') { 42 | Reflect.getOwnMetadata = function (metadataKey, target, targetKey) { 43 | if (target.hasOwnProperty(metadataContainerKey_1)) { 44 | return (target[metadataContainerKey_1][targetKey] || emptyMetadata_1)[metadataKey]; 45 | } 46 | }; 47 | } 48 | if (typeof Reflect.defineMetadata !== 'function') { 49 | Reflect.defineMetadata = function (metadataKey, metadataValue, target, targetKey) { 50 | var metadataContainer = target.hasOwnProperty(metadataContainerKey_1) ? target[metadataContainerKey_1] : (target[metadataContainerKey_1] = {}); 51 | var targetContainer = metadataContainer[targetKey] || (metadataContainer[targetKey] = {}); 52 | targetContainer[metadataKey] = metadataValue; 53 | }; 54 | } 55 | if (typeof Reflect.metadata !== 'function') { 56 | Reflect.metadata = function (metadataKey, metadataValue) { 57 | return function (target, targetKey) { 58 | Reflect.defineMetadata(metadataKey, metadataValue, target, targetKey); 59 | }; 60 | }; 61 | } 62 | } // endif FEATURE_NO_ESNEXT 63 | -------------------------------------------------------------------------------- /dist/commonjs/transformers/index.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export declare function transform(transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/commonjs/transformers/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | // tslint:disable:no-var-requires 4 | var transformers = [ 5 | require('./template').default, 6 | require('./title').default, 7 | require('./styles').default, 8 | require('./preboot').default 9 | ]; 10 | // tslint:enable:no-var-requires 11 | function transform(transformerCtx, options) { 12 | var html = options.template; 13 | for (var i = 0; i < transformers.length; i++) { 14 | html = transformers[i](html, transformerCtx, options); 15 | } 16 | return html; 17 | } 18 | exports.transform = transform; 19 | -------------------------------------------------------------------------------- /dist/commonjs/transformers/preboot.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/commonjs/transformers/preboot.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var preboot = require("preboot"); 4 | var utils_1 = require("./utils"); 5 | function default_1(html, transformerCtx, options) { 6 | if (options.preboot) { 7 | if (options.replayDelay === undefined) { 8 | options.replayDelay = 10; 9 | } 10 | // preboot catches all events that happens before Aurelia gets loaded client-side 11 | // so that they can be replayed afterwards 12 | var prebootOptions = Object.assign({ 13 | appRoot: options.appRoots || ['body'], 14 | eventSelectors: [ 15 | // for recording changes in form elements 16 | { selector: 'input,textarea', events: ['keypress', 'keyup', 'keydown', 'input', 'change'] }, 17 | { selector: 'select,option', events: ['change'] }, 18 | // when user hits return button in an input box 19 | { selector: 'input', events: ['keyup'], preventDefault: true, keyCodes: [13], freeze: true }, 20 | // for tracking focus (no need to replay) 21 | { selector: 'input,textarea', events: ['focusin', 'focusout', 'mousedown', 'mouseup'], noReplay: true }, 22 | { selector: 'button[type="submit"]', events: ['submit'], preventDefault: false, freeze: true }, 23 | { selector: 'form', events: ['submit'], preventDefault: true, freeze: true }, 24 | // user clicks on a button 25 | { selector: 'button:not([type="submit"])', events: ['click'], preventDefault: true, freeze: true } 26 | ] 27 | }, options.prebootOptions); 28 | var inlinePrebootCode = preboot.getInlineCode(prebootOptions); 29 | html = utils_1.appendToHead(html, "\r\n\r\n"); 30 | // preboot_browser can replay events that were stored by the preboot code 31 | html = utils_1.appendToBody(html, "\r\n\n "); 32 | } 33 | return html; 34 | } 35 | exports.default = default_1; 36 | ; 37 | -------------------------------------------------------------------------------- /dist/commonjs/transformers/styles.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/commonjs/transformers/styles.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | var utils_1 = require("./utils"); 4 | function default_1(html, transformerCtx, options) { 5 | var headStyleTags = Array.prototype.slice.call(transformerCtx.document.head.querySelectorAll('style')); 6 | // copy over any style tags 7 | for (var i = 0; i < headStyleTags.length; i++) { 8 | html = utils_1.appendToHead(html, headStyleTags[i].outerHTML); 9 | } 10 | return html; 11 | } 12 | exports.default = default_1; 13 | ; 14 | -------------------------------------------------------------------------------- /dist/commonjs/transformers/template.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/commonjs/transformers/template.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | function default_1(html, transformerCtx, options) { 4 | return options.template.replace('', transformerCtx.app); 5 | } 6 | exports.default = default_1; 7 | ; 8 | -------------------------------------------------------------------------------- /dist/commonjs/transformers/title.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/commonjs/transformers/title.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | function default_1(html, transformerCtx, options) { 4 | var title = transformerCtx.document.head.querySelector('title'); 5 | return html.replace('', title.innerHTML); 6 | } 7 | exports.default = default_1; 8 | ; 9 | -------------------------------------------------------------------------------- /dist/commonjs/transformers/utils.d.ts: -------------------------------------------------------------------------------- 1 | export declare function appendToBody(htmlString: string, toAppend: string): string; 2 | export declare function appendToHead(htmlString: string, toAppend: string): string; 3 | -------------------------------------------------------------------------------- /dist/commonjs/transformers/utils.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | function appendToBody(htmlString, toAppend) { 4 | return htmlString.replace('', toAppend + ""); 5 | } 6 | exports.appendToBody = appendToBody; 7 | function appendToHead(htmlString, toAppend) { 8 | return htmlString.replace('', toAppend + ""); 9 | } 10 | exports.appendToHead = appendToHead; 11 | -------------------------------------------------------------------------------- /dist/es2015/aurelia-ssr-engine.d.ts: -------------------------------------------------------------------------------- 1 | import './reflect'; 2 | import './property-descriptor'; 3 | import { RenderOptions, AppInitializationOptions } from './interfaces'; 4 | declare function render(options: RenderOptions, initOptions: AppInitializationOptions): Promise; 5 | export { render, AppInitializationOptions, RenderOptions }; 6 | -------------------------------------------------------------------------------- /dist/es2015/aurelia-ssr-engine.js: -------------------------------------------------------------------------------- 1 | import './reflect'; 2 | import './property-descriptor'; 3 | import { transform } from './transformers'; 4 | import { cleanup } from './cleanup'; 5 | function render(options, initOptions) { 6 | if (!options.url) { 7 | throw new Error('url is required when calling render()'); 8 | } 9 | if (!options.template) { 10 | throw new Error('template is required when calling render()'); 11 | } 12 | if (!initOptions.main) { 13 | throw new Error('main property is required'); 14 | } 15 | // this allows you to see aurelia logging in cmd/terminal 16 | // logging a lot of messages isn't good for performance though 17 | // so we need to set the loglevel in the app to something other than debug 18 | console.debug = console.log; 19 | // we'll want new instances of aurelia-pal and aurelia-pal-nodejs 20 | // because aurelia-pal holds the reference to the DOM 21 | delete require.cache[require.resolve('aurelia-pal')]; 22 | delete require.cache[require.resolve('aurelia-pal-nodejs')]; 23 | return start(initOptions, options.url.toString()) 24 | .then((ctx) => { 25 | const document = ctx.pal.DOM.global.document; 26 | setInputDefaultValues(document.body); 27 | const html = transform({ app: ctx.aurelia.host.outerHTML, document }, options); 28 | ctx.stop(); 29 | cleanup(options); 30 | return html; 31 | }); 32 | } 33 | // .value property does not map to @value attribute, .defaultValue does. 34 | // so we need to copy that value over if we want it to serialize into HTML 35 | // without this there isn't a value attribute on any of the input tags 36 | function setInputDefaultValues(body) { 37 | const inputTags = Array.prototype.slice.call(body.querySelectorAll('input')); 38 | for (let i = 0; i < inputTags.length; i++) { 39 | const input = inputTags[i]; 40 | if (input.value != null) { 41 | input.defaultValue = input.value; 42 | } 43 | } 44 | } 45 | function start(options, requestUrl) { 46 | const { initialize, start } = options.main(); 47 | const { PLATFORM } = initialize(); 48 | // url of jsdom should be equal to the request url 49 | // this dictates what page aurelia loads on startup 50 | PLATFORM.jsdom.reconfigure({ url: requestUrl }); 51 | return start(); 52 | } 53 | export { render }; 54 | -------------------------------------------------------------------------------- /dist/es2015/cleanup.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions } from './interfaces'; 2 | export declare function cleanup(options: RenderOptions): void; 3 | -------------------------------------------------------------------------------- /dist/es2015/cleanup.js: -------------------------------------------------------------------------------- 1 | // aurelia-binding array observer 2 | const pop = Array.prototype.pop; 3 | const push = Array.prototype.push; 4 | const reverse = Array.prototype.reverse; 5 | const shift = Array.prototype.shift; 6 | const sort = Array.prototype.sort; 7 | const splice = Array.prototype.splice; 8 | const unshift = Array.prototype.unshift; 9 | export function cleanup(options) { 10 | // aurelia-binding's array observer overrides the prototype of Array 11 | // which causes the Array global to have references to the app 12 | // and nodejs won't gc the app because of this 13 | Array.prototype.pop = pop; 14 | Array.prototype.push = push; 15 | Array.prototype.reverse = reverse; 16 | Array.prototype.shift = shift; 17 | Array.prototype.sort = sort; 18 | Array.prototype.splice = splice; 19 | Array.prototype.unshift = unshift; 20 | // delete the server bundle from cache 21 | if (process.mainModule) { 22 | rdelete(process.mainModule, options.bundlePath); 23 | } 24 | } 25 | /** 26 | * Recursively go over all node modules and delete a specific module 27 | * so it can be garbage collected 28 | * @param m 29 | * @param key 30 | */ 31 | function rdelete(m, key) { 32 | if (m.parent && m.parent.filename === require.resolve(key)) { 33 | delete m.parent; 34 | } 35 | for (let i = m.children.length - 1; i >= 0; i--) { 36 | if (m.children[i].filename === require.resolve(key)) { 37 | m.children.splice(i, 1); 38 | } 39 | else { 40 | rdelete(m.children[i], key); 41 | } 42 | } 43 | } 44 | ; 45 | -------------------------------------------------------------------------------- /dist/es2015/interfaces.d.ts: -------------------------------------------------------------------------------- 1 | import { IDom } from 'aurelia-pal-nodejs/dist/dom'; 2 | import { Aurelia } from 'aurelia-framework'; 3 | export interface RenderOptions { 4 | /** 5 | * The path to the bundle 6 | */ 7 | bundlePath: string; 8 | /** 9 | * The requested url 10 | */ 11 | url?: URL; 12 | /** 13 | * The template where indicates where server side rendered html will be inserted 14 | */ 15 | template: string; 16 | /** 17 | * Whether or not to use preboot. Preboot allows you to record (and playback) events 18 | * that occur before the client-app is loaded (defaults to false) 19 | */ 20 | preboot: boolean; 21 | /** 22 | * When using preboot, how long is the delay between Aurelia start and before the view has loaded 23 | */ 24 | replayDelay?: number; 25 | /** 26 | * Options that are passed to preboot 27 | */ 28 | prebootOptions?: any; 29 | /** 30 | * The queryselector(s) of the approot(s). Used by preboot 31 | * e.g. ['body'] 32 | */ 33 | appRoots?: string[]; 34 | } 35 | export interface AppInitializationOptions { 36 | /** 37 | * an initialize, start and stop function for the engine to call 38 | * to start and stop the aurelia app 39 | */ 40 | main: () => { 41 | initialize: () => { 42 | PLATFORM: any; 43 | }; 44 | start: () => Promise<{ 45 | aurelia: Aurelia; 46 | pal: AureliaPal; 47 | palNodeJS: AureliaPalNodeJS; 48 | stop: () => void; 49 | }>; 50 | stop: () => Promise; 51 | }; 52 | /** 53 | * The module id of the server main file (e.g. 'main') 54 | */ 55 | serverMainId?: string; 56 | } 57 | export interface TransformerContext { 58 | /** 59 | * The body of the server side rendered app 60 | */ 61 | app: string; 62 | /** 63 | * The JSDOM document 64 | */ 65 | document: any; 66 | } 67 | export interface Dom extends IDom { 68 | global: Window; 69 | } 70 | export interface AureliaPal { 71 | DOM: Dom; 72 | global: Window; 73 | reset(): void; 74 | } 75 | export interface AureliaPalNodeJS { 76 | configure(): void; 77 | initialize(): void; 78 | reset(): void; 79 | } 80 | -------------------------------------------------------------------------------- /dist/es2015/interfaces.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelia/ssr-engine/5394083a067136364d58650a78f9200b18033bd8/dist/es2015/interfaces.js -------------------------------------------------------------------------------- /dist/es2015/property-descriptor.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelia/ssr-engine/5394083a067136364d58650a78f9200b18033bd8/dist/es2015/property-descriptor.d.ts -------------------------------------------------------------------------------- /dist/es2015/property-descriptor.js: -------------------------------------------------------------------------------- 1 | // instead of letting aurelia-pal define Object.getPropertyDescriptor we'll do it here 2 | // so nodejs can garbage collect aurelia-pal 3 | Object.getPropertyDescriptor = function (subject, name) { 4 | let pd = Object.getOwnPropertyDescriptor(subject, name); 5 | let proto = Object.getPrototypeOf(subject); 6 | while (typeof pd === 'undefined' && proto !== null) { 7 | pd = Object.getOwnPropertyDescriptor(proto, name); 8 | proto = Object.getPrototypeOf(proto); 9 | } 10 | return pd; 11 | }; 12 | -------------------------------------------------------------------------------- /dist/es2015/reflect.d.ts: -------------------------------------------------------------------------------- 1 | declare var FEATURE_NO_ES2015: any; 2 | declare var FEATURE_NO_ESNEXT: any; 3 | declare namespace Reflect { 4 | function getOwnMetadata(metadataKey: any, target: any, targetKey: any): any; 5 | function defineMetadata(metadataKey: any, metadataValue: any, target: any, targetKey: any): any; 6 | function metadata(metadataKey: any, metadataValue: any): any; 7 | } 8 | -------------------------------------------------------------------------------- /dist/es2015/reflect.js: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | if (typeof FEATURE_NO_ES2015 === 'undefined') { 3 | const bind = Function.prototype.bind; 4 | if (typeof Reflect.defineProperty !== 'function') { 5 | Reflect.defineProperty = function (target, propertyKey, descriptor) { 6 | if (typeof target === 'object' ? target === null : typeof target !== 'function') { 7 | throw new TypeError('Reflect.defineProperty called on non-object'); 8 | } 9 | try { 10 | Object.defineProperty(target, propertyKey, descriptor); 11 | return true; 12 | } 13 | catch (e) { 14 | return false; 15 | } 16 | }; 17 | } 18 | if (typeof Reflect.construct !== 'function') { 19 | Reflect.construct = function (Target, args) { 20 | if (args) { 21 | switch (args.length) { 22 | case 0: return new Target(); 23 | case 1: return new Target(args[0]); 24 | case 2: return new Target(args[0], args[1]); 25 | case 3: return new Target(args[0], args[1], args[2]); 26 | case 4: return new Target(args[0], args[1], args[2], args[3]); 27 | } 28 | } 29 | var a = [null]; 30 | a.push.apply(a, args); 31 | return new (bind.apply(Target, a)); 32 | }; 33 | } 34 | if (typeof Reflect.ownKeys !== 'function') { 35 | Reflect.ownKeys = function (o) { return (Object.getOwnPropertyNames(o).concat(Object.getOwnPropertySymbols(o))); }; 36 | } 37 | } // endif FEATURE_NO_ES2015 38 | if (typeof FEATURE_NO_ESNEXT === 'undefined') { 39 | const emptyMetadata = Object.freeze({}); 40 | const metadataContainerKey = '__metadata__'; 41 | if (typeof Reflect.getOwnMetadata !== 'function') { 42 | Reflect.getOwnMetadata = function (metadataKey, target, targetKey) { 43 | if (target.hasOwnProperty(metadataContainerKey)) { 44 | return (target[metadataContainerKey][targetKey] || emptyMetadata)[metadataKey]; 45 | } 46 | }; 47 | } 48 | if (typeof Reflect.defineMetadata !== 'function') { 49 | Reflect.defineMetadata = function (metadataKey, metadataValue, target, targetKey) { 50 | let metadataContainer = target.hasOwnProperty(metadataContainerKey) ? target[metadataContainerKey] : (target[metadataContainerKey] = {}); 51 | let targetContainer = metadataContainer[targetKey] || (metadataContainer[targetKey] = {}); 52 | targetContainer[metadataKey] = metadataValue; 53 | }; 54 | } 55 | if (typeof Reflect.metadata !== 'function') { 56 | Reflect.metadata = function (metadataKey, metadataValue) { 57 | return function (target, targetKey) { 58 | Reflect.defineMetadata(metadataKey, metadataValue, target, targetKey); 59 | }; 60 | }; 61 | } 62 | } // endif FEATURE_NO_ESNEXT 63 | -------------------------------------------------------------------------------- /dist/es2015/transformers/index.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export declare function transform(transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/es2015/transformers/index.js: -------------------------------------------------------------------------------- 1 | // tslint:disable:no-var-requires 2 | const transformers = [ 3 | require('./template').default, 4 | require('./title').default, 5 | require('./styles').default, 6 | require('./preboot').default 7 | ]; 8 | // tslint:enable:no-var-requires 9 | export function transform(transformerCtx, options) { 10 | let html = options.template; 11 | for (let i = 0; i < transformers.length; i++) { 12 | html = transformers[i](html, transformerCtx, options); 13 | } 14 | return html; 15 | } 16 | -------------------------------------------------------------------------------- /dist/es2015/transformers/preboot.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/es2015/transformers/preboot.js: -------------------------------------------------------------------------------- 1 | import * as preboot from 'preboot'; 2 | import { appendToHead, appendToBody } from './utils'; 3 | export default function (html, transformerCtx, options) { 4 | if (options.preboot) { 5 | if (options.replayDelay === undefined) { 6 | options.replayDelay = 10; 7 | } 8 | // preboot catches all events that happens before Aurelia gets loaded client-side 9 | // so that they can be replayed afterwards 10 | const prebootOptions = Object.assign({ 11 | appRoot: options.appRoots || ['body'], 12 | eventSelectors: [ 13 | // for recording changes in form elements 14 | { selector: 'input,textarea', events: ['keypress', 'keyup', 'keydown', 'input', 'change'] }, 15 | { selector: 'select,option', events: ['change'] }, 16 | // when user hits return button in an input box 17 | { selector: 'input', events: ['keyup'], preventDefault: true, keyCodes: [13], freeze: true }, 18 | // for tracking focus (no need to replay) 19 | { selector: 'input,textarea', events: ['focusin', 'focusout', 'mousedown', 'mouseup'], noReplay: true }, 20 | { selector: 'button[type="submit"]', events: ['submit'], preventDefault: false, freeze: true }, 21 | { selector: 'form', events: ['submit'], preventDefault: true, freeze: true }, 22 | // user clicks on a button 23 | { selector: 'button:not([type="submit"])', events: ['click'], preventDefault: true, freeze: true } 24 | ] 25 | }, options.prebootOptions); 26 | const inlinePrebootCode = preboot.getInlineCode(prebootOptions); 27 | html = appendToHead(html, `\r\n\r\n`); 28 | // preboot_browser can replay events that were stored by the preboot code 29 | html = appendToBody(html, `\r\n 30 | `); 38 | } 39 | return html; 40 | } 41 | ; 42 | -------------------------------------------------------------------------------- /dist/es2015/transformers/styles.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/es2015/transformers/styles.js: -------------------------------------------------------------------------------- 1 | import { appendToHead } from './utils'; 2 | export default function (html, transformerCtx, options) { 3 | const headStyleTags = Array.prototype.slice.call(transformerCtx.document.head.querySelectorAll('style')); 4 | // copy over any style tags 5 | for (let i = 0; i < headStyleTags.length; i++) { 6 | html = appendToHead(html, headStyleTags[i].outerHTML); 7 | } 8 | return html; 9 | } 10 | ; 11 | -------------------------------------------------------------------------------- /dist/es2015/transformers/template.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/es2015/transformers/template.js: -------------------------------------------------------------------------------- 1 | export default function (html, transformerCtx, options) { 2 | return options.template.replace('', transformerCtx.app); 3 | } 4 | ; 5 | -------------------------------------------------------------------------------- /dist/es2015/transformers/title.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/es2015/transformers/title.js: -------------------------------------------------------------------------------- 1 | export default function (html, transformerCtx, options) { 2 | const title = transformerCtx.document.head.querySelector('title'); 3 | return html.replace('', title.innerHTML); 4 | } 5 | ; 6 | -------------------------------------------------------------------------------- /dist/es2015/transformers/utils.d.ts: -------------------------------------------------------------------------------- 1 | export declare function appendToBody(htmlString: string, toAppend: string): string; 2 | export declare function appendToHead(htmlString: string, toAppend: string): string; 3 | -------------------------------------------------------------------------------- /dist/es2015/transformers/utils.js: -------------------------------------------------------------------------------- 1 | export function appendToBody(htmlString, toAppend) { 2 | return htmlString.replace('', `${toAppend}`); 3 | } 4 | export function appendToHead(htmlString, toAppend) { 5 | return htmlString.replace('', `${toAppend}`); 6 | } 7 | -------------------------------------------------------------------------------- /dist/native-modules/aurelia-ssr-engine.d.ts: -------------------------------------------------------------------------------- 1 | import './reflect'; 2 | import './property-descriptor'; 3 | import { RenderOptions, AppInitializationOptions } from './interfaces'; 4 | declare function render(options: RenderOptions, initOptions: AppInitializationOptions): Promise; 5 | export { render, AppInitializationOptions, RenderOptions }; 6 | -------------------------------------------------------------------------------- /dist/native-modules/aurelia-ssr-engine.js: -------------------------------------------------------------------------------- 1 | import './reflect'; 2 | import './property-descriptor'; 3 | import { transform } from './transformers'; 4 | import { cleanup } from './cleanup'; 5 | function render(options, initOptions) { 6 | if (!options.url) { 7 | throw new Error('url is required when calling render()'); 8 | } 9 | if (!options.template) { 10 | throw new Error('template is required when calling render()'); 11 | } 12 | if (!initOptions.main) { 13 | throw new Error('main property is required'); 14 | } 15 | // this allows you to see aurelia logging in cmd/terminal 16 | // logging a lot of messages isn't good for performance though 17 | // so we need to set the loglevel in the app to something other than debug 18 | console.debug = console.log; 19 | // we'll want new instances of aurelia-pal and aurelia-pal-nodejs 20 | // because aurelia-pal holds the reference to the DOM 21 | delete require.cache[require.resolve('aurelia-pal')]; 22 | delete require.cache[require.resolve('aurelia-pal-nodejs')]; 23 | return start(initOptions, options.url.toString()) 24 | .then(function (ctx) { 25 | var document = ctx.pal.DOM.global.document; 26 | setInputDefaultValues(document.body); 27 | var html = transform({ app: ctx.aurelia.host.outerHTML, document: document }, options); 28 | ctx.stop(); 29 | cleanup(options); 30 | return html; 31 | }); 32 | } 33 | // .value property does not map to @value attribute, .defaultValue does. 34 | // so we need to copy that value over if we want it to serialize into HTML 35 | // without this there isn't a value attribute on any of the input tags 36 | function setInputDefaultValues(body) { 37 | var inputTags = Array.prototype.slice.call(body.querySelectorAll('input')); 38 | for (var i = 0; i < inputTags.length; i++) { 39 | var input = inputTags[i]; 40 | if (input.value != null) { 41 | input.defaultValue = input.value; 42 | } 43 | } 44 | } 45 | function start(options, requestUrl) { 46 | var _a = options.main(), initialize = _a.initialize, start = _a.start; 47 | var PLATFORM = initialize().PLATFORM; 48 | // url of jsdom should be equal to the request url 49 | // this dictates what page aurelia loads on startup 50 | PLATFORM.jsdom.reconfigure({ url: requestUrl }); 51 | return start(); 52 | } 53 | export { render }; 54 | -------------------------------------------------------------------------------- /dist/native-modules/cleanup.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions } from './interfaces'; 2 | export declare function cleanup(options: RenderOptions): void; 3 | -------------------------------------------------------------------------------- /dist/native-modules/cleanup.js: -------------------------------------------------------------------------------- 1 | // aurelia-binding array observer 2 | var pop = Array.prototype.pop; 3 | var push = Array.prototype.push; 4 | var reverse = Array.prototype.reverse; 5 | var shift = Array.prototype.shift; 6 | var sort = Array.prototype.sort; 7 | var splice = Array.prototype.splice; 8 | var unshift = Array.prototype.unshift; 9 | export function cleanup(options) { 10 | // aurelia-binding's array observer overrides the prototype of Array 11 | // which causes the Array global to have references to the app 12 | // and nodejs won't gc the app because of this 13 | Array.prototype.pop = pop; 14 | Array.prototype.push = push; 15 | Array.prototype.reverse = reverse; 16 | Array.prototype.shift = shift; 17 | Array.prototype.sort = sort; 18 | Array.prototype.splice = splice; 19 | Array.prototype.unshift = unshift; 20 | // delete the server bundle from cache 21 | if (process.mainModule) { 22 | rdelete(process.mainModule, options.bundlePath); 23 | } 24 | } 25 | /** 26 | * Recursively go over all node modules and delete a specific module 27 | * so it can be garbage collected 28 | * @param m 29 | * @param key 30 | */ 31 | function rdelete(m, key) { 32 | if (m.parent && m.parent.filename === require.resolve(key)) { 33 | delete m.parent; 34 | } 35 | for (var i = m.children.length - 1; i >= 0; i--) { 36 | if (m.children[i].filename === require.resolve(key)) { 37 | m.children.splice(i, 1); 38 | } 39 | else { 40 | rdelete(m.children[i], key); 41 | } 42 | } 43 | } 44 | ; 45 | -------------------------------------------------------------------------------- /dist/native-modules/interfaces.d.ts: -------------------------------------------------------------------------------- 1 | import { IDom } from 'aurelia-pal-nodejs/dist/dom'; 2 | import { Aurelia } from 'aurelia-framework'; 3 | export interface RenderOptions { 4 | /** 5 | * The path to the bundle 6 | */ 7 | bundlePath: string; 8 | /** 9 | * The requested url 10 | */ 11 | url?: URL; 12 | /** 13 | * The template where indicates where server side rendered html will be inserted 14 | */ 15 | template: string; 16 | /** 17 | * Whether or not to use preboot. Preboot allows you to record (and playback) events 18 | * that occur before the client-app is loaded (defaults to false) 19 | */ 20 | preboot: boolean; 21 | /** 22 | * When using preboot, how long is the delay between Aurelia start and before the view has loaded 23 | */ 24 | replayDelay?: number; 25 | /** 26 | * Options that are passed to preboot 27 | */ 28 | prebootOptions?: any; 29 | /** 30 | * The queryselector(s) of the approot(s). Used by preboot 31 | * e.g. ['body'] 32 | */ 33 | appRoots?: string[]; 34 | } 35 | export interface AppInitializationOptions { 36 | /** 37 | * an initialize, start and stop function for the engine to call 38 | * to start and stop the aurelia app 39 | */ 40 | main: () => { 41 | initialize: () => { 42 | PLATFORM: any; 43 | }; 44 | start: () => Promise<{ 45 | aurelia: Aurelia; 46 | pal: AureliaPal; 47 | palNodeJS: AureliaPalNodeJS; 48 | stop: () => void; 49 | }>; 50 | stop: () => Promise; 51 | }; 52 | /** 53 | * The module id of the server main file (e.g. 'main') 54 | */ 55 | serverMainId?: string; 56 | } 57 | export interface TransformerContext { 58 | /** 59 | * The body of the server side rendered app 60 | */ 61 | app: string; 62 | /** 63 | * The JSDOM document 64 | */ 65 | document: any; 66 | } 67 | export interface Dom extends IDom { 68 | global: Window; 69 | } 70 | export interface AureliaPal { 71 | DOM: Dom; 72 | global: Window; 73 | reset(): void; 74 | } 75 | export interface AureliaPalNodeJS { 76 | configure(): void; 77 | initialize(): void; 78 | reset(): void; 79 | } 80 | -------------------------------------------------------------------------------- /dist/native-modules/interfaces.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelia/ssr-engine/5394083a067136364d58650a78f9200b18033bd8/dist/native-modules/interfaces.js -------------------------------------------------------------------------------- /dist/native-modules/property-descriptor.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelia/ssr-engine/5394083a067136364d58650a78f9200b18033bd8/dist/native-modules/property-descriptor.d.ts -------------------------------------------------------------------------------- /dist/native-modules/property-descriptor.js: -------------------------------------------------------------------------------- 1 | // instead of letting aurelia-pal define Object.getPropertyDescriptor we'll do it here 2 | // so nodejs can garbage collect aurelia-pal 3 | Object.getPropertyDescriptor = function (subject, name) { 4 | var pd = Object.getOwnPropertyDescriptor(subject, name); 5 | var proto = Object.getPrototypeOf(subject); 6 | while (typeof pd === 'undefined' && proto !== null) { 7 | pd = Object.getOwnPropertyDescriptor(proto, name); 8 | proto = Object.getPrototypeOf(proto); 9 | } 10 | return pd; 11 | }; 12 | -------------------------------------------------------------------------------- /dist/native-modules/reflect.d.ts: -------------------------------------------------------------------------------- 1 | declare var FEATURE_NO_ES2015: any; 2 | declare var FEATURE_NO_ESNEXT: any; 3 | declare namespace Reflect { 4 | function getOwnMetadata(metadataKey: any, target: any, targetKey: any): any; 5 | function defineMetadata(metadataKey: any, metadataValue: any, target: any, targetKey: any): any; 6 | function metadata(metadataKey: any, metadataValue: any): any; 7 | } 8 | -------------------------------------------------------------------------------- /dist/native-modules/reflect.js: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | if (typeof FEATURE_NO_ES2015 === 'undefined') { 3 | var bind_1 = Function.prototype.bind; 4 | if (typeof Reflect.defineProperty !== 'function') { 5 | Reflect.defineProperty = function (target, propertyKey, descriptor) { 6 | if (typeof target === 'object' ? target === null : typeof target !== 'function') { 7 | throw new TypeError('Reflect.defineProperty called on non-object'); 8 | } 9 | try { 10 | Object.defineProperty(target, propertyKey, descriptor); 11 | return true; 12 | } 13 | catch (e) { 14 | return false; 15 | } 16 | }; 17 | } 18 | if (typeof Reflect.construct !== 'function') { 19 | Reflect.construct = function (Target, args) { 20 | if (args) { 21 | switch (args.length) { 22 | case 0: return new Target(); 23 | case 1: return new Target(args[0]); 24 | case 2: return new Target(args[0], args[1]); 25 | case 3: return new Target(args[0], args[1], args[2]); 26 | case 4: return new Target(args[0], args[1], args[2], args[3]); 27 | } 28 | } 29 | var a = [null]; 30 | a.push.apply(a, args); 31 | return new (bind_1.apply(Target, a)); 32 | }; 33 | } 34 | if (typeof Reflect.ownKeys !== 'function') { 35 | Reflect.ownKeys = function (o) { return (Object.getOwnPropertyNames(o).concat(Object.getOwnPropertySymbols(o))); }; 36 | } 37 | } // endif FEATURE_NO_ES2015 38 | if (typeof FEATURE_NO_ESNEXT === 'undefined') { 39 | var emptyMetadata_1 = Object.freeze({}); 40 | var metadataContainerKey_1 = '__metadata__'; 41 | if (typeof Reflect.getOwnMetadata !== 'function') { 42 | Reflect.getOwnMetadata = function (metadataKey, target, targetKey) { 43 | if (target.hasOwnProperty(metadataContainerKey_1)) { 44 | return (target[metadataContainerKey_1][targetKey] || emptyMetadata_1)[metadataKey]; 45 | } 46 | }; 47 | } 48 | if (typeof Reflect.defineMetadata !== 'function') { 49 | Reflect.defineMetadata = function (metadataKey, metadataValue, target, targetKey) { 50 | var metadataContainer = target.hasOwnProperty(metadataContainerKey_1) ? target[metadataContainerKey_1] : (target[metadataContainerKey_1] = {}); 51 | var targetContainer = metadataContainer[targetKey] || (metadataContainer[targetKey] = {}); 52 | targetContainer[metadataKey] = metadataValue; 53 | }; 54 | } 55 | if (typeof Reflect.metadata !== 'function') { 56 | Reflect.metadata = function (metadataKey, metadataValue) { 57 | return function (target, targetKey) { 58 | Reflect.defineMetadata(metadataKey, metadataValue, target, targetKey); 59 | }; 60 | }; 61 | } 62 | } // endif FEATURE_NO_ESNEXT 63 | -------------------------------------------------------------------------------- /dist/native-modules/transformers/index.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export declare function transform(transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/native-modules/transformers/index.js: -------------------------------------------------------------------------------- 1 | // tslint:disable:no-var-requires 2 | var transformers = [ 3 | require('./template').default, 4 | require('./title').default, 5 | require('./styles').default, 6 | require('./preboot').default 7 | ]; 8 | // tslint:enable:no-var-requires 9 | export function transform(transformerCtx, options) { 10 | var html = options.template; 11 | for (var i = 0; i < transformers.length; i++) { 12 | html = transformers[i](html, transformerCtx, options); 13 | } 14 | return html; 15 | } 16 | -------------------------------------------------------------------------------- /dist/native-modules/transformers/preboot.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/native-modules/transformers/preboot.js: -------------------------------------------------------------------------------- 1 | import * as preboot from 'preboot'; 2 | import { appendToHead, appendToBody } from './utils'; 3 | export default function (html, transformerCtx, options) { 4 | if (options.preboot) { 5 | if (options.replayDelay === undefined) { 6 | options.replayDelay = 10; 7 | } 8 | // preboot catches all events that happens before Aurelia gets loaded client-side 9 | // so that they can be replayed afterwards 10 | var prebootOptions = Object.assign({ 11 | appRoot: options.appRoots || ['body'], 12 | eventSelectors: [ 13 | // for recording changes in form elements 14 | { selector: 'input,textarea', events: ['keypress', 'keyup', 'keydown', 'input', 'change'] }, 15 | { selector: 'select,option', events: ['change'] }, 16 | // when user hits return button in an input box 17 | { selector: 'input', events: ['keyup'], preventDefault: true, keyCodes: [13], freeze: true }, 18 | // for tracking focus (no need to replay) 19 | { selector: 'input,textarea', events: ['focusin', 'focusout', 'mousedown', 'mouseup'], noReplay: true }, 20 | { selector: 'button[type="submit"]', events: ['submit'], preventDefault: false, freeze: true }, 21 | { selector: 'form', events: ['submit'], preventDefault: true, freeze: true }, 22 | // user clicks on a button 23 | { selector: 'button:not([type="submit"])', events: ['click'], preventDefault: true, freeze: true } 24 | ] 25 | }, options.prebootOptions); 26 | var inlinePrebootCode = preboot.getInlineCode(prebootOptions); 27 | html = appendToHead(html, "\r\n\r\n"); 28 | // preboot_browser can replay events that were stored by the preboot code 29 | html = appendToBody(html, "\r\n\n "); 30 | } 31 | return html; 32 | } 33 | ; 34 | -------------------------------------------------------------------------------- /dist/native-modules/transformers/styles.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/native-modules/transformers/styles.js: -------------------------------------------------------------------------------- 1 | import { appendToHead } from './utils'; 2 | export default function (html, transformerCtx, options) { 3 | var headStyleTags = Array.prototype.slice.call(transformerCtx.document.head.querySelectorAll('style')); 4 | // copy over any style tags 5 | for (var i = 0; i < headStyleTags.length; i++) { 6 | html = appendToHead(html, headStyleTags[i].outerHTML); 7 | } 8 | return html; 9 | } 10 | ; 11 | -------------------------------------------------------------------------------- /dist/native-modules/transformers/template.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/native-modules/transformers/template.js: -------------------------------------------------------------------------------- 1 | export default function (html, transformerCtx, options) { 2 | return options.template.replace('', transformerCtx.app); 3 | } 4 | ; 5 | -------------------------------------------------------------------------------- /dist/native-modules/transformers/title.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/native-modules/transformers/title.js: -------------------------------------------------------------------------------- 1 | export default function (html, transformerCtx, options) { 2 | var title = transformerCtx.document.head.querySelector('title'); 3 | return html.replace('', title.innerHTML); 4 | } 5 | ; 6 | -------------------------------------------------------------------------------- /dist/native-modules/transformers/utils.d.ts: -------------------------------------------------------------------------------- 1 | export declare function appendToBody(htmlString: string, toAppend: string): string; 2 | export declare function appendToHead(htmlString: string, toAppend: string): string; 3 | -------------------------------------------------------------------------------- /dist/native-modules/transformers/utils.js: -------------------------------------------------------------------------------- 1 | export function appendToBody(htmlString, toAppend) { 2 | return htmlString.replace('', toAppend + ""); 3 | } 4 | export function appendToHead(htmlString, toAppend) { 5 | return htmlString.replace('', toAppend + ""); 6 | } 7 | -------------------------------------------------------------------------------- /dist/system/aurelia-ssr-engine.d.ts: -------------------------------------------------------------------------------- 1 | import './reflect'; 2 | import './property-descriptor'; 3 | import { RenderOptions, AppInitializationOptions } from './interfaces'; 4 | declare function render(options: RenderOptions, initOptions: AppInitializationOptions): Promise; 5 | export { render, AppInitializationOptions, RenderOptions }; 6 | -------------------------------------------------------------------------------- /dist/system/aurelia-ssr-engine.js: -------------------------------------------------------------------------------- 1 | System.register(["./reflect", "./property-descriptor", "./transformers", "./cleanup"], function (exports_1, context_1) { 2 | "use strict"; 3 | var __moduleName = context_1 && context_1.id; 4 | function render(options, initOptions) { 5 | if (!options.url) { 6 | throw new Error('url is required when calling render()'); 7 | } 8 | if (!options.template) { 9 | throw new Error('template is required when calling render()'); 10 | } 11 | if (!initOptions.main) { 12 | throw new Error('main property is required'); 13 | } 14 | // this allows you to see aurelia logging in cmd/terminal 15 | // logging a lot of messages isn't good for performance though 16 | // so we need to set the loglevel in the app to something other than debug 17 | console.debug = console.log; 18 | // we'll want new instances of aurelia-pal and aurelia-pal-nodejs 19 | // because aurelia-pal holds the reference to the DOM 20 | delete require.cache[require.resolve('aurelia-pal')]; 21 | delete require.cache[require.resolve('aurelia-pal-nodejs')]; 22 | return start(initOptions, options.url.toString()) 23 | .then(function (ctx) { 24 | var document = ctx.pal.DOM.global.document; 25 | setInputDefaultValues(document.body); 26 | var html = transformers_1.transform({ app: ctx.aurelia.host.outerHTML, document: document }, options); 27 | ctx.stop(); 28 | cleanup_1.cleanup(options); 29 | return html; 30 | }); 31 | } 32 | exports_1("render", render); 33 | // .value property does not map to @value attribute, .defaultValue does. 34 | // so we need to copy that value over if we want it to serialize into HTML 35 | // without this there isn't a value attribute on any of the input tags 36 | function setInputDefaultValues(body) { 37 | var inputTags = Array.prototype.slice.call(body.querySelectorAll('input')); 38 | for (var i = 0; i < inputTags.length; i++) { 39 | var input = inputTags[i]; 40 | if (input.value != null) { 41 | input.defaultValue = input.value; 42 | } 43 | } 44 | } 45 | function start(options, requestUrl) { 46 | var _a = options.main(), initialize = _a.initialize, start = _a.start; 47 | var PLATFORM = initialize().PLATFORM; 48 | // url of jsdom should be equal to the request url 49 | // this dictates what page aurelia loads on startup 50 | PLATFORM.jsdom.reconfigure({ url: requestUrl }); 51 | return start(); 52 | } 53 | var transformers_1, cleanup_1; 54 | return { 55 | setters: [ 56 | function (_1) { 57 | }, 58 | function (_2) { 59 | }, 60 | function (transformers_1_1) { 61 | transformers_1 = transformers_1_1; 62 | }, 63 | function (cleanup_1_1) { 64 | cleanup_1 = cleanup_1_1; 65 | } 66 | ], 67 | execute: function () { 68 | } 69 | }; 70 | }); 71 | -------------------------------------------------------------------------------- /dist/system/cleanup.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions } from './interfaces'; 2 | export declare function cleanup(options: RenderOptions): void; 3 | -------------------------------------------------------------------------------- /dist/system/cleanup.js: -------------------------------------------------------------------------------- 1 | System.register([], function (exports_1, context_1) { 2 | "use strict"; 3 | var __moduleName = context_1 && context_1.id; 4 | function cleanup(options) { 5 | // aurelia-binding's array observer overrides the prototype of Array 6 | // which causes the Array global to have references to the app 7 | // and nodejs won't gc the app because of this 8 | Array.prototype.pop = pop; 9 | Array.prototype.push = push; 10 | Array.prototype.reverse = reverse; 11 | Array.prototype.shift = shift; 12 | Array.prototype.sort = sort; 13 | Array.prototype.splice = splice; 14 | Array.prototype.unshift = unshift; 15 | // delete the server bundle from cache 16 | if (process.mainModule) { 17 | rdelete(process.mainModule, options.bundlePath); 18 | } 19 | } 20 | exports_1("cleanup", cleanup); 21 | /** 22 | * Recursively go over all node modules and delete a specific module 23 | * so it can be garbage collected 24 | * @param m 25 | * @param key 26 | */ 27 | function rdelete(m, key) { 28 | if (m.parent && m.parent.filename === require.resolve(key)) { 29 | delete m.parent; 30 | } 31 | for (var i = m.children.length - 1; i >= 0; i--) { 32 | if (m.children[i].filename === require.resolve(key)) { 33 | m.children.splice(i, 1); 34 | } 35 | else { 36 | rdelete(m.children[i], key); 37 | } 38 | } 39 | } 40 | var pop, push, reverse, shift, sort, splice, unshift; 41 | return { 42 | setters: [], 43 | execute: function () { 44 | // aurelia-binding array observer 45 | pop = Array.prototype.pop; 46 | push = Array.prototype.push; 47 | reverse = Array.prototype.reverse; 48 | shift = Array.prototype.shift; 49 | sort = Array.prototype.sort; 50 | splice = Array.prototype.splice; 51 | unshift = Array.prototype.unshift; 52 | ; 53 | } 54 | }; 55 | }); 56 | -------------------------------------------------------------------------------- /dist/system/interfaces.d.ts: -------------------------------------------------------------------------------- 1 | import { IDom } from 'aurelia-pal-nodejs/dist/dom'; 2 | import { Aurelia } from 'aurelia-framework'; 3 | export interface RenderOptions { 4 | /** 5 | * The path to the bundle 6 | */ 7 | bundlePath: string; 8 | /** 9 | * The requested url 10 | */ 11 | url?: URL; 12 | /** 13 | * The template where indicates where server side rendered html will be inserted 14 | */ 15 | template: string; 16 | /** 17 | * Whether or not to use preboot. Preboot allows you to record (and playback) events 18 | * that occur before the client-app is loaded (defaults to false) 19 | */ 20 | preboot: boolean; 21 | /** 22 | * When using preboot, how long is the delay between Aurelia start and before the view has loaded 23 | */ 24 | replayDelay?: number; 25 | /** 26 | * Options that are passed to preboot 27 | */ 28 | prebootOptions?: any; 29 | /** 30 | * The queryselector(s) of the approot(s). Used by preboot 31 | * e.g. ['body'] 32 | */ 33 | appRoots?: string[]; 34 | } 35 | export interface AppInitializationOptions { 36 | /** 37 | * an initialize, start and stop function for the engine to call 38 | * to start and stop the aurelia app 39 | */ 40 | main: () => { 41 | initialize: () => { 42 | PLATFORM: any; 43 | }; 44 | start: () => Promise<{ 45 | aurelia: Aurelia; 46 | pal: AureliaPal; 47 | palNodeJS: AureliaPalNodeJS; 48 | stop: () => void; 49 | }>; 50 | stop: () => Promise; 51 | }; 52 | /** 53 | * The module id of the server main file (e.g. 'main') 54 | */ 55 | serverMainId?: string; 56 | } 57 | export interface TransformerContext { 58 | /** 59 | * The body of the server side rendered app 60 | */ 61 | app: string; 62 | /** 63 | * The JSDOM document 64 | */ 65 | document: any; 66 | } 67 | export interface Dom extends IDom { 68 | global: Window; 69 | } 70 | export interface AureliaPal { 71 | DOM: Dom; 72 | global: Window; 73 | reset(): void; 74 | } 75 | export interface AureliaPalNodeJS { 76 | configure(): void; 77 | initialize(): void; 78 | reset(): void; 79 | } 80 | -------------------------------------------------------------------------------- /dist/system/interfaces.js: -------------------------------------------------------------------------------- 1 | System.register([], function (exports_1, context_1) { 2 | "use strict"; 3 | var __moduleName = context_1 && context_1.id; 4 | return { 5 | setters: [], 6 | execute: function () { 7 | } 8 | }; 9 | }); 10 | -------------------------------------------------------------------------------- /dist/system/property-descriptor.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelia/ssr-engine/5394083a067136364d58650a78f9200b18033bd8/dist/system/property-descriptor.d.ts -------------------------------------------------------------------------------- /dist/system/property-descriptor.js: -------------------------------------------------------------------------------- 1 | // instead of letting aurelia-pal define Object.getPropertyDescriptor we'll do it here 2 | // so nodejs can garbage collect aurelia-pal 3 | Object.getPropertyDescriptor = function (subject, name) { 4 | var pd = Object.getOwnPropertyDescriptor(subject, name); 5 | var proto = Object.getPrototypeOf(subject); 6 | while (typeof pd === 'undefined' && proto !== null) { 7 | pd = Object.getOwnPropertyDescriptor(proto, name); 8 | proto = Object.getPrototypeOf(proto); 9 | } 10 | return pd; 11 | }; 12 | -------------------------------------------------------------------------------- /dist/system/reflect.d.ts: -------------------------------------------------------------------------------- 1 | declare var FEATURE_NO_ES2015: any; 2 | declare var FEATURE_NO_ESNEXT: any; 3 | declare namespace Reflect { 4 | function getOwnMetadata(metadataKey: any, target: any, targetKey: any): any; 5 | function defineMetadata(metadataKey: any, metadataValue: any, target: any, targetKey: any): any; 6 | function metadata(metadataKey: any, metadataValue: any): any; 7 | } 8 | -------------------------------------------------------------------------------- /dist/system/reflect.js: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | if (typeof FEATURE_NO_ES2015 === 'undefined') { 3 | var bind_1 = Function.prototype.bind; 4 | if (typeof Reflect.defineProperty !== 'function') { 5 | Reflect.defineProperty = function (target, propertyKey, descriptor) { 6 | if (typeof target === 'object' ? target === null : typeof target !== 'function') { 7 | throw new TypeError('Reflect.defineProperty called on non-object'); 8 | } 9 | try { 10 | Object.defineProperty(target, propertyKey, descriptor); 11 | return true; 12 | } 13 | catch (e) { 14 | return false; 15 | } 16 | }; 17 | } 18 | if (typeof Reflect.construct !== 'function') { 19 | Reflect.construct = function (Target, args) { 20 | if (args) { 21 | switch (args.length) { 22 | case 0: return new Target(); 23 | case 1: return new Target(args[0]); 24 | case 2: return new Target(args[0], args[1]); 25 | case 3: return new Target(args[0], args[1], args[2]); 26 | case 4: return new Target(args[0], args[1], args[2], args[3]); 27 | } 28 | } 29 | var a = [null]; 30 | a.push.apply(a, args); 31 | return new (bind_1.apply(Target, a)); 32 | }; 33 | } 34 | if (typeof Reflect.ownKeys !== 'function') { 35 | Reflect.ownKeys = function (o) { return (Object.getOwnPropertyNames(o).concat(Object.getOwnPropertySymbols(o))); }; 36 | } 37 | } // endif FEATURE_NO_ES2015 38 | if (typeof FEATURE_NO_ESNEXT === 'undefined') { 39 | var emptyMetadata_1 = Object.freeze({}); 40 | var metadataContainerKey_1 = '__metadata__'; 41 | if (typeof Reflect.getOwnMetadata !== 'function') { 42 | Reflect.getOwnMetadata = function (metadataKey, target, targetKey) { 43 | if (target.hasOwnProperty(metadataContainerKey_1)) { 44 | return (target[metadataContainerKey_1][targetKey] || emptyMetadata_1)[metadataKey]; 45 | } 46 | }; 47 | } 48 | if (typeof Reflect.defineMetadata !== 'function') { 49 | Reflect.defineMetadata = function (metadataKey, metadataValue, target, targetKey) { 50 | var metadataContainer = target.hasOwnProperty(metadataContainerKey_1) ? target[metadataContainerKey_1] : (target[metadataContainerKey_1] = {}); 51 | var targetContainer = metadataContainer[targetKey] || (metadataContainer[targetKey] = {}); 52 | targetContainer[metadataKey] = metadataValue; 53 | }; 54 | } 55 | if (typeof Reflect.metadata !== 'function') { 56 | Reflect.metadata = function (metadataKey, metadataValue) { 57 | return function (target, targetKey) { 58 | Reflect.defineMetadata(metadataKey, metadataValue, target, targetKey); 59 | }; 60 | }; 61 | } 62 | } // endif FEATURE_NO_ESNEXT 63 | -------------------------------------------------------------------------------- /dist/system/transformers/index.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export declare function transform(transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/system/transformers/index.js: -------------------------------------------------------------------------------- 1 | System.register([], function (exports_1, context_1) { 2 | "use strict"; 3 | var __moduleName = context_1 && context_1.id; 4 | // tslint:enable:no-var-requires 5 | function transform(transformerCtx, options) { 6 | var html = options.template; 7 | for (var i = 0; i < transformers.length; i++) { 8 | html = transformers[i](html, transformerCtx, options); 9 | } 10 | return html; 11 | } 12 | exports_1("transform", transform); 13 | var transformers; 14 | return { 15 | setters: [], 16 | execute: function () { 17 | // tslint:disable:no-var-requires 18 | transformers = [ 19 | require('./template').default, 20 | require('./title').default, 21 | require('./styles').default, 22 | require('./preboot').default 23 | ]; 24 | } 25 | }; 26 | }); 27 | -------------------------------------------------------------------------------- /dist/system/transformers/preboot.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/system/transformers/preboot.js: -------------------------------------------------------------------------------- 1 | System.register(["preboot", "./utils"], function (exports_1, context_1) { 2 | "use strict"; 3 | var __moduleName = context_1 && context_1.id; 4 | function default_1(html, transformerCtx, options) { 5 | if (options.preboot) { 6 | if (options.replayDelay === undefined) { 7 | options.replayDelay = 10; 8 | } 9 | // preboot catches all events that happens before Aurelia gets loaded client-side 10 | // so that they can be replayed afterwards 11 | var prebootOptions = Object.assign({ 12 | appRoot: options.appRoots || ['body'], 13 | eventSelectors: [ 14 | // for recording changes in form elements 15 | { selector: 'input,textarea', events: ['keypress', 'keyup', 'keydown', 'input', 'change'] }, 16 | { selector: 'select,option', events: ['change'] }, 17 | // when user hits return button in an input box 18 | { selector: 'input', events: ['keyup'], preventDefault: true, keyCodes: [13], freeze: true }, 19 | // for tracking focus (no need to replay) 20 | { selector: 'input,textarea', events: ['focusin', 'focusout', 'mousedown', 'mouseup'], noReplay: true }, 21 | { selector: 'button[type="submit"]', events: ['submit'], preventDefault: false, freeze: true }, 22 | { selector: 'form', events: ['submit'], preventDefault: true, freeze: true }, 23 | // user clicks on a button 24 | { selector: 'button:not([type="submit"])', events: ['click'], preventDefault: true, freeze: true } 25 | ] 26 | }, options.prebootOptions); 27 | var inlinePrebootCode = preboot.getInlineCode(prebootOptions); 28 | html = utils_1.appendToHead(html, "\r\n\r\n"); 29 | // preboot_browser can replay events that were stored by the preboot code 30 | html = utils_1.appendToBody(html, "\r\n\n "); 31 | } 32 | return html; 33 | } 34 | exports_1("default", default_1); 35 | var preboot, utils_1; 36 | return { 37 | setters: [ 38 | function (preboot_1) { 39 | preboot = preboot_1; 40 | }, 41 | function (utils_1_1) { 42 | utils_1 = utils_1_1; 43 | } 44 | ], 45 | execute: function () { 46 | ; 47 | } 48 | }; 49 | }); 50 | -------------------------------------------------------------------------------- /dist/system/transformers/styles.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/system/transformers/styles.js: -------------------------------------------------------------------------------- 1 | System.register(["./utils"], function (exports_1, context_1) { 2 | "use strict"; 3 | var __moduleName = context_1 && context_1.id; 4 | function default_1(html, transformerCtx, options) { 5 | var headStyleTags = Array.prototype.slice.call(transformerCtx.document.head.querySelectorAll('style')); 6 | // copy over any style tags 7 | for (var i = 0; i < headStyleTags.length; i++) { 8 | html = utils_1.appendToHead(html, headStyleTags[i].outerHTML); 9 | } 10 | return html; 11 | } 12 | exports_1("default", default_1); 13 | var utils_1; 14 | return { 15 | setters: [ 16 | function (utils_1_1) { 17 | utils_1 = utils_1_1; 18 | } 19 | ], 20 | execute: function () { 21 | ; 22 | } 23 | }; 24 | }); 25 | -------------------------------------------------------------------------------- /dist/system/transformers/template.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/system/transformers/template.js: -------------------------------------------------------------------------------- 1 | System.register([], function (exports_1, context_1) { 2 | "use strict"; 3 | var __moduleName = context_1 && context_1.id; 4 | function default_1(html, transformerCtx, options) { 5 | return options.template.replace('', transformerCtx.app); 6 | } 7 | exports_1("default", default_1); 8 | return { 9 | setters: [], 10 | execute: function () { 11 | ; 12 | } 13 | }; 14 | }); 15 | -------------------------------------------------------------------------------- /dist/system/transformers/title.d.ts: -------------------------------------------------------------------------------- 1 | import { RenderOptions, TransformerContext } from '../interfaces'; 2 | export default function (html: string, transformerCtx: TransformerContext, options: RenderOptions): string; 3 | -------------------------------------------------------------------------------- /dist/system/transformers/title.js: -------------------------------------------------------------------------------- 1 | System.register([], function (exports_1, context_1) { 2 | "use strict"; 3 | var __moduleName = context_1 && context_1.id; 4 | function default_1(html, transformerCtx, options) { 5 | var title = transformerCtx.document.head.querySelector('title'); 6 | return html.replace('', title.innerHTML); 7 | } 8 | exports_1("default", default_1); 9 | return { 10 | setters: [], 11 | execute: function () { 12 | ; 13 | } 14 | }; 15 | }); 16 | -------------------------------------------------------------------------------- /dist/system/transformers/utils.d.ts: -------------------------------------------------------------------------------- 1 | export declare function appendToBody(htmlString: string, toAppend: string): string; 2 | export declare function appendToHead(htmlString: string, toAppend: string): string; 3 | -------------------------------------------------------------------------------- /dist/system/transformers/utils.js: -------------------------------------------------------------------------------- 1 | System.register([], function (exports_1, context_1) { 2 | "use strict"; 3 | var __moduleName = context_1 && context_1.id; 4 | function appendToBody(htmlString, toAppend) { 5 | return htmlString.replace('', toAppend + ""); 6 | } 7 | exports_1("appendToBody", appendToBody); 8 | function appendToHead(htmlString, toAppend) { 9 | return htmlString.replace('', toAppend + ""); 10 | } 11 | exports_1("appendToHead", appendToHead); 12 | return { 13 | setters: [], 14 | execute: function () { 15 | } 16 | }; 17 | }); 18 | -------------------------------------------------------------------------------- /doc/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # 0.1.0 (2018-01-21) 3 | 4 | 5 | ### Features 6 | 7 | * **ssr-engine:** add implementation of ssr engine ([5436fd8](https://github.com/aurelia/ssr-engine/commit/5436fd8)) 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /doc/shape-defs.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const packageJsonPath = path.resolve(__dirname, '../package.json'); 6 | 7 | try { 8 | const packageName = require(packageJsonPath).name; 9 | const dtsPath = path.resolve(__dirname, `../dist/doc-temp/${packageName}.d.ts`); 10 | let defs = fs.readFileSync(dtsPath).toString(); 11 | 12 | // aggregate external imports 13 | const packages = {}; 14 | const importRegex = /^\s*import\s+\{([^}]+)\}\s*from\s*'([\w|-]+)'/gm; 15 | let importMatch = importRegex.exec(defs); 16 | while (importMatch) { 17 | const packageName = importMatch[2]; 18 | const imports = packages[packageName] || (packages[packageName] = []); 19 | const bindings = importMatch[1].split(',').map(x => x.trim()); 20 | for (let binding of bindings) { 21 | if (imports.indexOf(binding) === -1) { 22 | imports.push(binding); 23 | } 24 | } 25 | importMatch = importRegex.exec(defs); 26 | } 27 | 28 | // remove leading declare module 29 | defs = defs.replace(/^declare module ".*" \{/, ''); 30 | // remove "} declare module {" 31 | defs = defs.replace(/\}\r?\ndeclare module ".*" \{/g, ''); 32 | // remove closing "}" 33 | defs = defs.replace(/\}\r?\n$/, ''); 34 | // remove imports 35 | defs = defs.replace(/^\s+import.*;$/gm, ''); 36 | // remove "export *" 37 | defs = defs.replace(/^\s+export \*.*;$/gm, ''); 38 | 39 | // write imports 40 | for (let packageName in packages) { 41 | if (packages.hasOwnProperty(packageName)) { 42 | const imports = packages[packageName]; 43 | defs = `import {${imports.sort()}} from '${packageName}';\n` + defs; 44 | } 45 | } 46 | 47 | fs.writeFileSync(dtsPath, defs); 48 | console.log(`Shaped the dist/doc-temp/${packageName}.d.ts file.`); 49 | } catch (e) { 50 | console.error(`Unable to shape the .d.ts file.`); 51 | console.error(e.message); 52 | } 53 | -------------------------------------------------------------------------------- /doc/shape-doc.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const path = require('path'); 4 | const fs = require('fs'); 5 | const packageJsonPath = path.resolve(__dirname, '../package.json'); 6 | const apiJsonPath = path.resolve(__dirname, './api.json'); 7 | 8 | try { 9 | const packageName = require(packageJsonPath).name; 10 | let json = require(apiJsonPath).children[0]; 11 | 12 | json = { 13 | name: packageName, 14 | children: json.children, 15 | groups: json.groups 16 | }; 17 | 18 | const str = JSON.stringify(json) + '\n'; 19 | fs.writeFileSync(apiJsonPath, str); 20 | console.log('Shaped the doc/api.json file.'); 21 | } catch (e) { 22 | console.error('Unable to shape the api.json. The file probably has an incorrect format or doesn\'t exist.'); 23 | console.error(e.message); 24 | } 25 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | require('require-dir')('build/tasks'); 2 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Sun Aug 28 2016 19:03:27 GMT-0400 (Eastern Daylight Time) 3 | 4 | module.exports = function(config) { 5 | config.set({ 6 | 7 | // base path that will be used to resolve all patterns (eg. files, exclude) 8 | basePath: '', 9 | 10 | 11 | // frameworks to use 12 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 13 | frameworks: ['jasmine', 'requirejs'], 14 | 15 | 16 | // list of files / patterns to load in the browser 17 | files: [ 18 | 'dist/test/test/main.js', 19 | { pattern: 'dist/test/**/*.js', included: false, watched: true }, 20 | //{ pattern: 'dist/test/**/*.html', included: false, watched: true }, 21 | { pattern: 'node_modules/**/*.js', included: false, watched: false }, 22 | ], 23 | 24 | 25 | // list of files to exclude 26 | exclude: [ 27 | ], 28 | 29 | 30 | // preprocess matching files before serving them to the browser 31 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 32 | preprocessors: { 33 | }, 34 | 35 | 36 | // test results reporter to use 37 | // possible values: 'dots', 'progress' 38 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 39 | reporters: ['progress'], 40 | 41 | 42 | // web server port 43 | port: 9876, 44 | 45 | 46 | // enable / disable colors in the output (reporters and logs) 47 | colors: true, 48 | 49 | 50 | // level of logging 51 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 52 | logLevel: config.LOG_INFO, 53 | 54 | 55 | // enable / disable watching file and executing tests whenever any file changes 56 | autoWatch: true, 57 | 58 | 59 | // start these browsers 60 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 61 | browsers: ['Chrome'], 62 | 63 | 64 | // Continuous Integration mode 65 | // if true, Karma captures browsers, runs the tests and exits 66 | singleRun: false, 67 | 68 | // Concurrency level 69 | // how many browser should be started simultaneous 70 | concurrency: Infinity 71 | }) 72 | } 73 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aurelia-ssr-engine", 3 | "version": "0.1.0", 4 | "description": "The server-side rendering engine for Aurelia.", 5 | "keywords": [ 6 | "aurelia", 7 | "server", 8 | "spa" 9 | ], 10 | "homepage": "http://aurelia.io", 11 | "bugs": { 12 | "url": "https://github.com/aurelia/ssr-engine/issues" 13 | }, 14 | "license": "MIT", 15 | "author": "Rob Eisenberg (http://robeisenberg.com/)", 16 | "main": "dist/commonjs/aurelia-ssr-engine.js", 17 | "typings": "dist/commonjs/aurelia-ssr-engine.d.ts", 18 | "repository": { 19 | "type": "git", 20 | "url": "http://github.com/aurelia/ssr-engine" 21 | }, 22 | "scripts": { 23 | "lint": "cross-env tslint --project tsconfig.json", 24 | "pretest": "cross-env npm run lint", 25 | "test": "cross-env rimraf dist && tsc && karma start --single-run", 26 | "test-watch": "concurrently \"cross-env ./node_modules/.bin/tsc --watch\" \"cross-env ./node_modules/.bin/karma start\"", 27 | "prebuild:amd": "cross-env rimraf dist/amd", 28 | "build:amd": "cross-env tsc --project tsconfig.build.json --outDir dist/amd --module amd", 29 | "postbuild:amd": "cross-env copyfiles --up 1 src/**/*.html src/**/*.css dist/amd", 30 | "prebuild:commonjs": "cross-env rimraf dist/commonjs", 31 | "build:commonjs": "cross-env tsc --project tsconfig.build.json --outDir dist/commonjs --module commonjs", 32 | "postbuild:commonjs": "cross-env copyfiles --up 1 src/**/*.html src/**/*.css dist/commonjs", 33 | "prebuild:es2015": "cross-env rimraf dist/es2015", 34 | "build:es2015": "cross-env tsc --project tsconfig.build.json --outDir dist/es2015 --module es2015 --target es2015", 35 | "postbuild:es2015": "cross-env copyfiles --up 1 src/**/*.html src/**/*.css dist/es2015", 36 | "prebuild:native-modules": "cross-env rimraf dist/native-modules", 37 | "build:native-modules": "cross-env tsc --project tsconfig.build.json --outDir dist/native-modules --module es2015", 38 | "postbuild:native-modules": "cross-env copyfiles --up 1 src/**/*.html src/**/*.css dist/native-modules", 39 | "prebuild:system": "cross-env rimraf dist/system", 40 | "build:system": "cross-env tsc --project tsconfig.build.json --outDir dist/system --module system", 41 | "postbuild:system": "cross-env copyfiles --up 1 src/**/*.html src/**/*.css dist/system", 42 | "prebuild": "cross-env rimraf dist && rimraf node_modules/@types/!node", 43 | "build": "concurrently \"npm run build:amd\" \"npm run build:commonjs\" \"npm run build:es2015\" \"npm run build:native-modules\" \"npm run build:system\"", 44 | "predoc": "cross-env rimraf doc/api.json && rimraf dist/doc-temp && tsc --project tsconfig.build.json --outFile dist/doc-temp/aurelia-ssr-engine.js && node doc/shape-defs", 45 | "doc": "cross-env typedoc --json doc/api.json --excludeExternals --includeDeclarations --mode modules --name aurelia-ssr-engine-docs --tsconfig tsconfig.doc.json", 46 | "postdoc": "cross-env node doc/shape-doc && rimraf dist/doc-temp", 47 | "changelog": "cross-env conventional-changelog -p angular -i doc/CHANGELOG.md -s", 48 | "bump-version": "npm --no-git-tag-version version", 49 | "preprepare-release": "cross-env npm run test", 50 | "prepare-release": "cross-env npm run changelog && npm run build" 51 | }, 52 | "dependencies": { 53 | "aurelia-framework": "^1.0.0", 54 | "aurelia-pal": "^1.0.0", 55 | "aurelia-pal-nodejs": "^1.0.0-beta.1.0.0", 56 | "preboot": "^4.5.2" 57 | }, 58 | "devDependencies": { 59 | "@types/jasmine": "2.5.47", 60 | "@types/node": "8.0.14", 61 | "concurrently": "^3.1.0", 62 | "conventional-changelog-cli": "^1.2.0", 63 | "copyfiles": "^1.0.0", 64 | "cross-env": "^3.1.0", 65 | "del": "^2.2.1", 66 | "eslint": "^3.1.1", 67 | "jasmine-core": "^2.4.1", 68 | "merge2": "^1.0.2", 69 | "object.assign": "^4.0.4", 70 | "require-dir": "^0.3.0", 71 | "rimraf": "^2.5.4", 72 | "run-sequence": "^1.2.2", 73 | "through2": "^2.0.1", 74 | "tslint": "4.5.1", 75 | "typedoc": "^0.5.9", 76 | "typescript": "^2.4.2" 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/aurelia-ssr-engine.ts: -------------------------------------------------------------------------------- 1 | import './reflect'; 2 | import './property-descriptor'; 3 | import {RenderOptions, AppInitializationOptions, AureliaPalNodeJS, AureliaPal} from './interfaces'; 4 | import {transform} from './transformers'; 5 | import {Aurelia} from 'aurelia-framework'; 6 | import {cleanup} from './cleanup'; 7 | 8 | function render(options: RenderOptions, initOptions: AppInitializationOptions) { 9 | if (!options.url) { 10 | throw new Error('url is required when calling render()'); 11 | } 12 | if (!options.template) { 13 | throw new Error('template is required when calling render()'); 14 | } 15 | if (!initOptions.main) { 16 | throw new Error('main property is required'); 17 | } 18 | 19 | // this allows you to see aurelia logging in cmd/terminal 20 | // logging a lot of messages isn't good for performance though 21 | // so we need to set the loglevel in the app to something other than debug 22 | console.debug = console.log; 23 | 24 | // we'll want new instances of aurelia-pal and aurelia-pal-nodejs 25 | // because aurelia-pal holds the reference to the DOM 26 | delete require.cache[require.resolve('aurelia-pal')]; 27 | delete require.cache[require.resolve('aurelia-pal-nodejs')]; 28 | 29 | return start(initOptions, options.url.toString()) 30 | .then((ctx: { aurelia: Aurelia, pal: AureliaPal, palNodeJS: AureliaPalNodeJS, stop: () => void }) => { 31 | const document = ctx.pal.DOM.global.document; 32 | 33 | setInputDefaultValues(document.body as HTMLBodyElement); 34 | 35 | const html = transform({ app: ctx.aurelia.host.outerHTML, document}, options); 36 | 37 | ctx.stop(); 38 | cleanup(options); 39 | 40 | return html; 41 | }); 42 | } 43 | 44 | // .value property does not map to @value attribute, .defaultValue does. 45 | // so we need to copy that value over if we want it to serialize into HTML 46 | // without this there isn't a value attribute on any of the input tags 47 | function setInputDefaultValues(body: HTMLBodyElement) { 48 | const inputTags = Array.prototype.slice.call(body.querySelectorAll('input')); 49 | for (let i = 0; i < inputTags.length; i++) { 50 | const input = inputTags[i]; 51 | if (input.value != null) { 52 | input.defaultValue = input.value; 53 | } 54 | } 55 | } 56 | 57 | function start(options: AppInitializationOptions, requestUrl: string) { 58 | const {initialize, start} = options.main(); 59 | 60 | const {PLATFORM} = initialize(); 61 | 62 | // url of jsdom should be equal to the request url 63 | // this dictates what page aurelia loads on startup 64 | PLATFORM.jsdom.reconfigure({ url: requestUrl }); 65 | 66 | return start(); 67 | } 68 | 69 | export { 70 | render, 71 | AppInitializationOptions, 72 | RenderOptions 73 | }; 74 | -------------------------------------------------------------------------------- /src/cleanup.ts: -------------------------------------------------------------------------------- 1 | import {RenderOptions} from './interfaces'; 2 | 3 | // aurelia-binding array observer 4 | const pop = Array.prototype.pop; 5 | const push = Array.prototype.push; 6 | const reverse = Array.prototype.reverse; 7 | const shift = Array.prototype.shift; 8 | const sort = Array.prototype.sort; 9 | const splice = Array.prototype.splice; 10 | const unshift = Array.prototype.unshift; 11 | 12 | export function cleanup(options: RenderOptions) { 13 | // aurelia-binding's array observer overrides the prototype of Array 14 | // which causes the Array global to have references to the app 15 | // and nodejs won't gc the app because of this 16 | Array.prototype.pop = pop; 17 | Array.prototype.push = push; 18 | Array.prototype.reverse = reverse; 19 | Array.prototype.shift = shift; 20 | Array.prototype.sort = sort; 21 | Array.prototype.splice = splice; 22 | Array.prototype.unshift = unshift; 23 | 24 | // delete the server bundle from cache 25 | if (process.mainModule) { 26 | rdelete(process.mainModule, options.bundlePath); 27 | } 28 | } 29 | 30 | /** 31 | * Recursively go over all node modules and delete a specific module 32 | * so it can be garbage collected 33 | * @param m 34 | * @param key 35 | */ 36 | function rdelete(m: NodeModule, key: string) { 37 | if (m.parent && m.parent.filename === require.resolve(key)) { 38 | delete m.parent; 39 | } 40 | 41 | for (let i = m.children.length - 1; i >= 0; i--) { 42 | if (m.children[i].filename === require.resolve(key)) { 43 | m.children.splice(i, 1); 44 | } else { 45 | rdelete(m.children[i], key); 46 | } 47 | } 48 | }; 49 | -------------------------------------------------------------------------------- /src/interfaces.ts: -------------------------------------------------------------------------------- 1 | import {IDom} from 'aurelia-pal-nodejs/dist/dom'; 2 | import {Aurelia} from 'aurelia-framework'; 3 | 4 | export interface RenderOptions { 5 | /** 6 | * The path to the bundle 7 | */ 8 | bundlePath: string; 9 | 10 | /** 11 | * The requested url 12 | */ 13 | url?: URL; 14 | 15 | /** 16 | * The template where indicates where server side rendered html will be inserted 17 | */ 18 | template: string; 19 | 20 | /** 21 | * Whether or not to use preboot. Preboot allows you to record (and playback) events 22 | * that occur before the client-app is loaded (defaults to false) 23 | */ 24 | preboot: boolean; 25 | 26 | /** 27 | * When using preboot, how long is the delay between Aurelia start and before the view has loaded 28 | */ 29 | replayDelay?: number; 30 | 31 | /** 32 | * Options that are passed to preboot 33 | */ 34 | prebootOptions?: any; 35 | 36 | /** 37 | * The queryselector(s) of the approot(s). Used by preboot 38 | * e.g. ['body'] 39 | */ 40 | appRoots?: string[]; 41 | } 42 | 43 | export interface AppInitializationOptions { 44 | /** 45 | * an initialize, start and stop function for the engine to call 46 | * to start and stop the aurelia app 47 | */ 48 | main: () => { 49 | initialize: () => { PLATFORM: any }, 50 | start: () => Promise<{ aurelia: Aurelia, pal: AureliaPal, palNodeJS: AureliaPalNodeJS, stop: () => void }>, 51 | stop: () => Promise 52 | }; 53 | 54 | /** 55 | * The module id of the server main file (e.g. 'main') 56 | */ 57 | serverMainId?: string; 58 | } 59 | 60 | export interface TransformerContext { 61 | /** 62 | * The body of the server side rendered app 63 | */ 64 | app: string; 65 | 66 | /** 67 | * The JSDOM document 68 | */ 69 | document: any; 70 | } 71 | 72 | export interface Dom extends IDom { 73 | global: Window; 74 | } 75 | 76 | export interface AureliaPal { 77 | DOM: Dom; 78 | 79 | global: Window; 80 | 81 | reset(): void; 82 | } 83 | 84 | export interface AureliaPalNodeJS { 85 | configure(): void; 86 | 87 | initialize(): void; 88 | 89 | reset(): void; 90 | } 91 | -------------------------------------------------------------------------------- /src/property-descriptor.ts: -------------------------------------------------------------------------------- 1 | // instead of letting aurelia-pal define Object.getPropertyDescriptor we'll do it here 2 | // so nodejs can garbage collect aurelia-pal 3 | (Object as any).getPropertyDescriptor = function(subject: any, name: any) { 4 | let pd = Object.getOwnPropertyDescriptor(subject, name); 5 | let proto = Object.getPrototypeOf(subject); 6 | while (typeof pd === 'undefined' && proto !== null) { 7 | pd = Object.getOwnPropertyDescriptor(proto, name); 8 | proto = Object.getPrototypeOf(proto); 9 | } 10 | return pd; 11 | }; 12 | -------------------------------------------------------------------------------- /src/reflect.ts: -------------------------------------------------------------------------------- 1 | /* tslint:disable */ 2 | 3 | declare var FEATURE_NO_ES2015: any; 4 | declare var FEATURE_NO_ESNEXT: any; 5 | 6 | declare namespace Reflect { 7 | function getOwnMetadata(metadataKey: any, target: any, targetKey: any): any; 8 | function defineMetadata(metadataKey: any, metadataValue: any, target: any, targetKey: any): any; 9 | function metadata(metadataKey: any, metadataValue: any): any; 10 | } 11 | 12 | if (typeof FEATURE_NO_ES2015 === 'undefined') { 13 | 14 | const bind = Function.prototype.bind; 15 | 16 | if (typeof Reflect.defineProperty !== 'function') { 17 | Reflect.defineProperty = function(target, propertyKey, descriptor) { 18 | if (typeof target === 'object' ? target === null : typeof target !== 'function') { 19 | throw new TypeError('Reflect.defineProperty called on non-object'); 20 | } 21 | try { 22 | Object.defineProperty(target, propertyKey, descriptor); 23 | return true; 24 | } catch (e) { 25 | return false; 26 | } 27 | }; 28 | } 29 | 30 | if (typeof Reflect.construct !== 'function') { 31 | Reflect.construct = function(Target: any, args) { 32 | if (args) { 33 | switch (args.length){ 34 | case 0: return new Target(); 35 | case 1: return new Target(args[0]); 36 | case 2: return new Target(args[0], args[1]); 37 | case 3: return new Target(args[0], args[1], args[2]); 38 | case 4: return new Target(args[0], args[1], args[2], args[3]); 39 | } 40 | } 41 | 42 | var a = [null]; 43 | a.push.apply(a, args); 44 | return new (bind.apply(Target, a)); 45 | }; 46 | } 47 | 48 | if (typeof Reflect.ownKeys !== 'function') { 49 | Reflect.ownKeys = function(o) { return (Object.getOwnPropertyNames(o).concat((Object).getOwnPropertySymbols(o))); } 50 | } 51 | 52 | } // endif FEATURE_NO_ES2015 53 | 54 | if (typeof FEATURE_NO_ESNEXT === 'undefined') { 55 | 56 | const emptyMetadata = Object.freeze({}); 57 | const metadataContainerKey = '__metadata__'; 58 | 59 | if (typeof Reflect.getOwnMetadata !== 'function') { 60 | Reflect.getOwnMetadata = function(metadataKey, target, targetKey) { 61 | if (target.hasOwnProperty(metadataContainerKey)) { 62 | return (target[metadataContainerKey][targetKey] || emptyMetadata)[metadataKey]; 63 | } 64 | }; 65 | } 66 | 67 | if (typeof Reflect.defineMetadata !== 'function') { 68 | Reflect.defineMetadata = function(metadataKey, metadataValue, target, targetKey) { 69 | let metadataContainer = target.hasOwnProperty(metadataContainerKey) ? target[metadataContainerKey] : (target[metadataContainerKey] = {}); 70 | let targetContainer = metadataContainer[targetKey] || (metadataContainer[targetKey] = {}); 71 | targetContainer[metadataKey] = metadataValue; 72 | }; 73 | } 74 | 75 | if (typeof Reflect.metadata !== 'function') { 76 | Reflect.metadata = function(metadataKey, metadataValue) { 77 | return function(target: any, targetKey: any) { 78 | Reflect.defineMetadata(metadataKey, metadataValue, target, targetKey); 79 | }; 80 | }; 81 | } 82 | 83 | } // endif FEATURE_NO_ESNEXT 84 | -------------------------------------------------------------------------------- /src/transformers/index.ts: -------------------------------------------------------------------------------- 1 | import {RenderOptions, TransformerContext} from '../interfaces'; 2 | 3 | // tslint:disable:no-var-requires 4 | const transformers = [ 5 | require('./template').default, 6 | require('./title').default, 7 | require('./styles').default, 8 | require('./preboot').default 9 | ]; 10 | // tslint:enable:no-var-requires 11 | 12 | export function transform(transformerCtx: TransformerContext, options: RenderOptions) { 13 | let html = options.template; 14 | 15 | for (let i = 0; i < transformers.length; i++) { 16 | html = transformers[i](html, transformerCtx, options); 17 | } 18 | 19 | return html; 20 | } 21 | -------------------------------------------------------------------------------- /src/transformers/preboot.ts: -------------------------------------------------------------------------------- 1 | import * as preboot from 'preboot'; 2 | import {appendToHead, appendToBody} from './utils'; 3 | import {RenderOptions, TransformerContext} from '../interfaces'; 4 | 5 | export default function(html: string, transformerCtx: TransformerContext, options: RenderOptions) { 6 | if (options.preboot) { 7 | if (options.replayDelay === undefined) { 8 | options.replayDelay = 10; 9 | } 10 | 11 | // preboot catches all events that happens before Aurelia gets loaded client-side 12 | // so that they can be replayed afterwards 13 | const prebootOptions = Object.assign({ 14 | appRoot: options.appRoots || ['body'], 15 | eventSelectors: [ 16 | // for recording changes in form elements 17 | { selector: 'input,textarea', events: ['keypress', 'keyup', 'keydown', 'input', 'change'] }, 18 | { selector: 'select,option', events: ['change'] }, 19 | // when user hits return button in an input box 20 | { selector: 'input', events: ['keyup'], preventDefault: true, keyCodes: [13], freeze: true }, 21 | // for tracking focus (no need to replay) 22 | { selector: 'input,textarea', events: ['focusin', 'focusout', 'mousedown', 'mouseup'], noReplay: true }, 23 | { selector: 'button[type="submit"]', events: ['submit'], preventDefault: false, freeze: true }, 24 | { selector: 'form', events: ['submit'], preventDefault: true, freeze: true }, 25 | // user clicks on a button 26 | { selector: 'button:not([type="submit"])', events: ['click'], preventDefault: true, freeze: true } 27 | ] 28 | }, options.prebootOptions); 29 | const inlinePrebootCode = preboot.getInlineCode(prebootOptions); 30 | html = appendToHead(html, `\r\n\r\n`); 31 | 32 | // preboot_browser can replay events that were stored by the preboot code 33 | html = appendToBody(html, `\r\n 34 | `); 42 | } 43 | 44 | return html; 45 | }; 46 | -------------------------------------------------------------------------------- /src/transformers/styles.ts: -------------------------------------------------------------------------------- 1 | import {appendToHead} from './utils'; 2 | import {RenderOptions, TransformerContext} from '../interfaces'; 3 | 4 | export default function(html: string, transformerCtx: TransformerContext, options: RenderOptions) { 5 | const headStyleTags = Array.prototype.slice.call(transformerCtx.document.head.querySelectorAll('style')); 6 | 7 | // copy over any style tags 8 | for (let i = 0; i < headStyleTags.length; i++) { 9 | html = appendToHead(html, headStyleTags[i].outerHTML); 10 | } 11 | 12 | return html; 13 | }; 14 | -------------------------------------------------------------------------------- /src/transformers/template.ts: -------------------------------------------------------------------------------- 1 | 2 | import {RenderOptions, TransformerContext} from '../interfaces'; 3 | 4 | export default function(html: string, transformerCtx: TransformerContext, options: RenderOptions) { 5 | return options.template.replace('', transformerCtx.app); 6 | }; 7 | -------------------------------------------------------------------------------- /src/transformers/title.ts: -------------------------------------------------------------------------------- 1 | 2 | import {RenderOptions, TransformerContext} from '../interfaces'; 3 | 4 | export default function(html: string, transformerCtx: TransformerContext, options: RenderOptions) { 5 | const title = transformerCtx.document.head.querySelector('title'); 6 | 7 | return html.replace('', title.innerHTML); 8 | }; 9 | -------------------------------------------------------------------------------- /src/transformers/utils.ts: -------------------------------------------------------------------------------- 1 | export function appendToBody(htmlString: string, toAppend: string) { 2 | return htmlString.replace('', `${toAppend}`); 3 | } 4 | 5 | export function appendToHead(htmlString: string, toAppend: string) { 6 | return htmlString.replace('', `${toAppend}`); 7 | } 8 | -------------------------------------------------------------------------------- /test/ssr-configuration.spec.d.ts: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelia/ssr-engine/5394083a067136364d58650a78f9200b18033bd8/test/ssr-configuration.spec.d.ts -------------------------------------------------------------------------------- /test/ssr-configuration.spec.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aurelia/ssr-engine/5394083a067136364d58650a78f9200b18033bd8/test/ssr-configuration.spec.js -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "amd", 4 | "moduleResolution": "node", 5 | "target": "es5", 6 | "lib": [ 7 | "es2017", 8 | "dom" 9 | ], 10 | "outDir": "dist/amd", 11 | "noImplicitAny": true, 12 | "allowUnreachableCode": false, 13 | "allowUnusedLabels": false, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": false, 16 | "noImplicitReturns": true, 17 | "strictNullChecks": true, 18 | "declaration": true, 19 | "forceConsistentCasingInFileNames": true, 20 | "experimentalDecorators": true, 21 | "noEmitHelpers": false, 22 | "stripInternal": true, 23 | "skipLibCheck": true 24 | }, 25 | "exclude": [ 26 | ".vscode", 27 | "dist", 28 | "doc", 29 | "node_modules", 30 | "test" 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /tsconfig.doc.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "target": "es2015", 5 | "experimentalDecorators": true, 6 | "stripInternal": true, 7 | "skipLibCheck": true 8 | }, 9 | "include": [ 10 | "./dist/doc-temp/*.d.ts" 11 | ], 12 | "exclude": [] 13 | } 14 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.build", 3 | "compilerOptions": { 4 | "outDir": "dist/test", 5 | "allowUnreachableCode": true, 6 | "allowUnusedLabels": true, 7 | "skipLibCheck": true 8 | }, 9 | "exclude": [ 10 | ".vscode", 11 | "dist", 12 | "doc", 13 | "node_modules" 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:latest", 3 | "rules": { 4 | "arrow-parens": [true, "ban-single-arg-parens"], 5 | "interface-name": [true, "never-prefix"], 6 | "member-ordering": ["fields-first"], 7 | "no-shadowed-variable": false, 8 | "no-string-literal": false, 9 | "object-literal-sort-keys": false, 10 | "ordered-imports": [false], 11 | "prefer-for-of": false, 12 | "no-console": [ 13 | false 14 | ], 15 | "only-arrow-functions": [ 16 | false 17 | ], 18 | "no-reference": false, 19 | "quotemark": [true, "single"], 20 | "trailing-comma": [false], 21 | "whitespace": [true, "check-branch", "check-decl", "check-operator", "check-module", "check-separator", "check-type"] 22 | } 23 | } --------------------------------------------------------------------------------