654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## AtScript Playground
2 |
3 | This is an empty repo to make it easy to experiment with AtScript.
4 |
5 |
6 | ### Initial setup
7 |
8 | ```bash
9 | # Clone the repo...
10 | git clone https://github.com/vojtajina/atscript-playground.git
11 | cd atscript-playground
12 |
13 | # Then, you need to install all the dependencies...
14 | npm install
15 |
16 | # If you want to be able to use global commands `karma` and `gulp`...
17 | npm install -g karma-cli gulp
18 | ```
19 |
20 | ### The minimal example
21 |
22 | Our example consists of two files:
23 |
24 | * `atscript-playground/src/something.ats` defines a simple class that returns the sum of two input values
25 | * `atscript-playground/src/main.ats` imports that class and prints a message to the console
26 |
27 | ### Running the example in the browser
28 | To run in the browser, you need to first build the project. This creates a `build/` directory that contains the transpiled `*.js` files that are created from your AtScript project.
29 |
30 | ```bash
31 | # Do initial build, start a webserver and re-build on every file change...
32 | gulp build serve watch
33 | ```
34 | Open a browser and look in the console log to see the result.
35 |
36 | ### Running the tests
37 | The tests are in `atscript-playground/test/something_spec.ats`. Run them using Karma, like so:
38 | ```bash
39 | karma start
40 | ```
41 | Karma opens a browser window for running tests. To see the actual test output (and errors), look for the log in the terminal window where you issued the `karma start` command.
42 |
43 | ### What are all the pieces involved?
44 |
45 | #### [Traceur]
46 | Transpiles AtScript code into regular ES5 (today's JavaScript) so that it can be run in a current browser.
47 |
48 | #### [RequireJS]
49 | Traceur is configured to transpile AtScript modules into AMD syntax and we use RequireJS to load the code in the browser. This is just temporary until we improve the ES Module Loader polyfill ([more details](https://github.com/angular/atscript-playground/issues/3)).
50 |
51 | #### [Assert] library
52 | When `typeAssertions: true` option is used, Traceur generates run-time type assertions such as `assert.type(x, Object)`. The assert library does the actual run-time check. Of course, you can use your own assert library.
53 |
54 | The idea with type assertions is that you only use them during the development/testing and when deploying, you use `typeAssertions: false`.
55 |
56 | #### [Karma]
57 | Test runner that runs the tests in specified browsers, every time that you change a file.
58 |
59 | #### [Gulp]
60 | Task runner to make defining and running the tasks simpler.
61 |
62 |
63 | [AtScript]: http://atscript.org
64 | [Traceur]: https://github.com/google/traceur-compiler
65 | [RequireJS]: http://requirejs.org
66 | [Assert]: https://github.com/angular/assert
67 | [Karma]: http://karma-runner.github.io/
68 | [Gulp]: http://gulpjs.com
69 |
--------------------------------------------------------------------------------
/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "traceur": {
3 | "modules": "amd",
4 | "script": false,
5 | "types": true,
6 | "typeAssertions": true,
7 | "typeAssertionModule": "assert",
8 | "annotations": true,
9 | "sourceMaps": "file"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | var gulp = require('gulp');
2 | var traceur = require('gulp-traceur');
3 | var connect = require('gulp-connect');
4 | var rename_ = require('gulp-rename');
5 |
6 | var TRACEUR_OPTIONS = require('./config').traceur;
7 | var PATH = {
8 | BUILD: './build/',
9 | SRC: './src/**/*.ats',
10 | TEST: './test/**/*.ats'
11 | };
12 |
13 | // A wrapper around gulp-rename to support `dirnamePrefix`.
14 | function rename(obj) {
15 | return rename_(function(parsedPath) {
16 | return {
17 | extname: obj.extname || parsedPath.extname,
18 | dirname: (obj.dirnamePrefix || '') + parsedPath.dirname,
19 | basename: parsedPath.basename
20 | };
21 | });
22 | }
23 |
24 |
25 | // TRANSPILE AT SCRIPT
26 | gulp.task('build/src', function() {
27 | gulp.src(PATH.SRC, {base: '.'})
28 | // Rename before Traceur, so that Traceur has the knowledge of both input and output paths.
29 | .pipe(rename({extname: '.js', dirnamePrefix: PATH.BUILD}))
30 | .pipe(traceur(TRACEUR_OPTIONS))
31 | .pipe(gulp.dest('.'));
32 | });
33 |
34 | gulp.task('build/test', function() {
35 | gulp.src(PATH.TEST, {base: '.'})
36 | // Rename before Traceur, so that Traceur has the knowledge of both input and output paths.
37 | .pipe(rename({extname: '.js', dirnamePrefix: PATH.BUILD}))
38 | .pipe(traceur(TRACEUR_OPTIONS))
39 | .pipe(gulp.dest('.'));
40 | });
41 |
42 | gulp.task('build', ['build/src', 'build/test']);
43 |
44 | // WATCH FILES FOR CHANGES
45 | gulp.task('watch', function() {
46 | gulp.watch(PATH.SRC, ['build']);
47 | });
48 |
49 |
50 | // WEB SERVER
51 | gulp.task('serve', function() {
52 | connect.server({
53 | root: [__dirname],
54 | port: 8000,
55 | livereload: false
56 | });
57 | });
58 |
59 |
60 | gulp.task('default', ['serve', 'watch']);
61 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | AtScript playground
4 |
5 |
6 |
7 | Check out the console;-)
8 |
9 |
10 |
11 |
12 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | // Karma configuration
2 | // Generated on Fri Mar 14 2014 15:01:19 GMT-0700 (PDT)
3 |
4 | var traceurOptions = require('./config').traceur;
5 |
6 | module.exports = function(config) {
7 |
8 | var isWebstorm = /karma-intellij/.test(process.argv[1]);
9 | var files;
10 |
11 | if (isWebstorm) {
12 | // Running within WebStorm - WebStorm takes care of transpiling.
13 | // Serve already transpiled files, including source maps.
14 | files = [
15 | {pattern: 'build/src/**/*.js', included: false},
16 | {pattern: 'build/test/**/*.js', included: false},
17 | {pattern: 'build/src/**/*.map', included: false},
18 | {pattern: 'build/test/**/*.map', included: false}
19 | ];
20 | } else {
21 | // Running outside WebStorm (eg. from commandline).
22 | // Karma transpiles the *.ats sources with karma-traceur-preprocessor.
23 | files = [
24 | {pattern: 'src/**/*.ats', included: false},
25 | {pattern: 'test/**/*.ats', included: false}
26 | ];
27 | }
28 |
29 | config.set({
30 | frameworks: ['jasmine', 'requirejs', 'traceur', 'sourcemaps'],
31 |
32 | files: [
33 | // The entry point that dynamically imports all the specs.
34 | {pattern: 'test/main.js', included: true},
35 |
36 | // The runtime assertion library.
37 | {pattern: 'node_modules/rtts-assert/dist/amd/assert.js', included: false}
38 | ].concat(files),
39 |
40 | preprocessors: {
41 | '**/*.ats': ['traceur']
42 | },
43 |
44 | browsers: ['Chrome'],
45 |
46 | traceurPreprocessor: {
47 | options: traceurOptions,
48 | transformPath: function(path) {
49 | // Traceur preprocessor is only used when running Karma outside of WebStorm.
50 | // We change the path to `build/**` so that the paths are the same as with WebStorm.
51 | return path.replace(config.basePath, config.basePath + '/build')
52 | .replace(/\.ats$/, '.js');
53 | }
54 | }
55 | });
56 |
57 | config.plugins.push(require('./karma_sourcemaps'));
58 | };
59 |
--------------------------------------------------------------------------------
/karma_sourcemaps.js:
--------------------------------------------------------------------------------
1 | var SOURCE_MAP = /\.map$/;
2 |
3 | function findFile(files, path) {
4 | for (var i = 0, ii = files.length; i < ii; i++) {
5 | if (files[i].path === path) {
6 | return files[i];
7 | }
8 | }
9 |
10 | return null;
11 | }
12 |
13 | function createSourceMapsPlugin(emitter, basePath, logger) {
14 | var log = logger.create('sourcemaps');
15 |
16 | emitter.on('file_list_modified', function(filesPromise) {
17 | filesPromise.then(function(files) {
18 | files.served.forEach(function(file) {
19 | if (SOURCE_MAP.test(file.path)) {
20 | var sourceMap = JSON.parse(file.content);
21 |
22 | if (!sourceMap) {
23 | log.warn('Invalid source map file', file.originalPath);
24 | return;
25 | }
26 |
27 | var sourceFile = findFile(files.served, basePath + '/' + sourceMap.file);
28 |
29 | if (!sourceFile) {
30 | log.warn('Can not find source file for map', file.originalPath);
31 | return
32 | }
33 |
34 | sourceMap.sourceRoot = basePath;
35 | sourceFile.sourceMap = sourceMap;
36 | }
37 | });
38 | });
39 | });
40 | }
41 |
42 | createSourceMapsPlugin.$inject = ['emitter', 'config.basePath', 'logger'];
43 |
44 | module.exports = {
45 | 'framework:sourcemaps': ['factory', createSourceMapsPlugin]
46 | };
47 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "atscript-playground",
3 | "private": true,
4 | "version": "0.0.0",
5 | "description": "A repo to play with AtScript.",
6 | "homepage": "https://github.com/vojtajina/atscript-playground",
7 | "repository": {
8 | "type": "git",
9 | "url": "git://github.com/vojtajina/atscript-playground.git"
10 | },
11 | "bugs": {
12 | "url": "https://github.com/vojtajina/atscript-playground/issues"
13 | },
14 | "dependencies": {
15 | "traceur": "^0.0.72",
16 | "requirejs": "^2.1.15"
17 | },
18 | "devDependencies": {
19 | "assert": "angular/assert#dist",
20 | "gulp": "^3.8.9",
21 | "gulp-connect": "^2.0.6",
22 | "gulp-rename": "^1.2.0",
23 | "gulp-traceur": "vojtajina/gulp-traceur#fix-sourcemaps",
24 | "karma": "^0.12.24",
25 | "karma-chrome-launcher": "^0.1.5",
26 | "karma-jasmine": "^0.2.2",
27 | "karma-requirejs": "^0.2.2",
28 | "karma-traceur-preprocessor": "^0.4.0"
29 | },
30 | "scripts": {
31 | "test": "karma start --single-run"
32 | },
33 | "author": "Vojta Jína ",
34 | "license": "Apache-2.0"
35 | }
36 |
--------------------------------------------------------------------------------
/src/main.ats:
--------------------------------------------------------------------------------
1 | import {Something} from './something';
2 |
3 | var s = new Something();
4 |
5 | console.log('1 + 2 is ', s.sum(1, 2));
6 |
--------------------------------------------------------------------------------
/src/something.ats:
--------------------------------------------------------------------------------
1 | export class Something {
2 | sum(a: number, b: number): number {
3 | return a + b;
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/test/main.js:
--------------------------------------------------------------------------------
1 | // The entry point for unit tests.
2 |
3 | var TEST_REGEXP = /_spec\.js$/;
4 |
5 | function pathToModule(path) {
6 | return path.replace(/^\/base\//, '').replace(/\.js$/, '');
7 | }
8 |
9 | function onlySpecs(path) {
10 | return TEST_REGEXP.test(path);
11 | }
12 |
13 | function getAllSpecs() {
14 | return Object.keys(window.__karma__.files)
15 | .filter(onlySpecs)
16 | .map(pathToModule);
17 | }
18 |
19 | require.config({
20 | // Karma serves files under `/base`, which is the `basePath` from `karma-conf.js` file.
21 | baseUrl: '/base',
22 |
23 | paths: {
24 | assert: './node_modules/rtts-assert/dist/amd/assert'
25 | },
26 |
27 | // Dynamically load all test files.
28 | deps: getAllSpecs(),
29 |
30 | // Kickoff Jasmine, once all spec files are loaded.
31 | callback: window.__karma__.start
32 | });
33 |
--------------------------------------------------------------------------------
/test/something_spec.ats:
--------------------------------------------------------------------------------
1 | import {Something} from '../src/something';
2 |
3 | describe('something', function() {
4 | var something;
5 |
6 | beforeEach(function() {
7 | something = new Something();
8 | });
9 |
10 | it('should work', function() {
11 | expect(something.sum(1, 2)).toBe(3);
12 | });
13 |
14 | xit('should fail', function() {
15 | // See the stack traces with source maps...
16 | something.sum(1, 'invalid');
17 | });
18 | });
19 |
--------------------------------------------------------------------------------
/wait_for_watcher.js:
--------------------------------------------------------------------------------
1 | // Wait for any outstanding Traceur watchers.
2 | // Hack to work-around https://youtrack.jetbrains.com/issue/WEB-14104
3 |
4 | // How it works?
5 | // WebStorm Traceur watcher writes LOCK_FILE when running and delete LOCK_FILE once all watchers are finished.
6 | // WebStorm Karma configuration waits for this process to finish (i.e. till the LOCK_FILE is removed).
7 |
8 | var fs = require('fs');
9 | var LOCK_FILE = '.atscriptwatcher';
10 | var START_DELAY = 500;
11 |
12 | setTimeout(function() {
13 | if (!fs.existsSync(LOCK_FILE)) {
14 | // Watcher was not running.
15 | process.exit(0);
16 | }
17 |
18 | fs.watch(LOCK_FILE, function() {
19 | if (!fs.existsSync(LOCK_FILE)) {
20 | // All watchers have finished.
21 | process.exit(0);
22 | }
23 | });
24 | }, START_DELAY);
25 |
26 |
27 |
--------------------------------------------------------------------------------
/webstorm_traceur.js:
--------------------------------------------------------------------------------
1 | var inputFilename = process.argv[2];
2 | var outputFilename = process.argv[3] || inputFilename.replace(/\.ats$/, '.js');
3 |
4 | var path = require('path');
5 | var sourceRoot = path.relative(path.dirname(outputFilename), '');
6 |
7 | var TraceurNodeCompiler = require('traceur/src/node/NodeCompiler').NodeCompiler;
8 | var options = require('./config.json').traceur;
9 | var compiler = new TraceurNodeCompiler(options);
10 |
11 |
12 | // Increment the lock to delay Karma unit tests until all watchers are finished.
13 | var LOCK_FILE = '.atscriptwatcher';
14 | var fs = require('fs');
15 |
16 | if (fs.existsSync(LOCK_FILE)) {
17 | var lockCount = parseInt(fs.readFileSync(LOCK_FILE).toString(), 10);
18 | lockCount++;
19 | fs.writeFileSync(LOCK_FILE, lockCount.toString());
20 | } else {
21 | fs.writeFileSync(LOCK_FILE, '1');
22 | }
23 |
24 |
25 | // TODO(vojta): Fix this in Traceur instead.
26 | // Traceur generates source map file in the CWD.
27 | // This hacks Traceur to generate source map file in the output file directory, respecting the `sourceRoot` option.
28 | var writeFile = require('traceur/src/node/file-util').writeFile;
29 | compiler.writeTreeToFile = function(tree, filename) {
30 | filename = this.normalize(filename);
31 | // This is changed.
32 | // Pass sourceRoot to `this.write`. Traceur does not pass it through.
33 | var compiledCode = this.write(tree, filename, sourceRoot);
34 | if (this.options_.sourceMaps === 'file') {
35 | var sourcemap = this.getSourceMap();
36 | if (sourcemap) {
37 | // This is changed.
38 | // Generate the source map in the same folder as the output JS file.
39 | writeFile(filename.replace(/\.js$/, '.map'), sourcemap);
40 | }
41 | }
42 |
43 | writeFile(filename, compiledCode);
44 | };
45 |
46 |
47 | compiler.compileSingleFile(inputFilename, outputFilename, function(err) {
48 | console.error(err);
49 | });
50 |
51 | // Release/decrement the lock.
52 | var lockCount = parseInt(fs.readFileSync(LOCK_FILE).toString(), 10);
53 | lockCount--;
54 |
55 | if (lockCount === 0) {
56 | fs.unlinkSync(LOCK_FILE);
57 | } else {
58 | fs.writeFileSync(LOCK_FILE, lockCount.toString());
59 | }
60 |
--------------------------------------------------------------------------------