From 5612071cf304cdd18b0a41afc3149d3b85adc78d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 18 Aug 2015 11:01:44 -0700 Subject: [PATCH 01/30] Add line-ending-selector to packageDependencies --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 207627305..88853e79c 100644 --- a/package.json +++ b/package.json @@ -101,6 +101,7 @@ "image-view": "0.54.0", "incompatible-packages": "0.24.1", "keybinding-resolver": "0.33.0", + "line-ending-selector": "0.0.3", "link": "0.30.0", "markdown-preview": "0.150.0", "metrics": "0.51.0", From 7adcbc60a360079cf30a8136d3a60468a491b5fe Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 18 Aug 2015 14:31:03 -0700 Subject: [PATCH 02/30] Compile babel files in place during compile task --- build/Gruntfile.coffee | 19 ++++++++++++++++++- build/lib/uses-babel.coffee | 18 ++++++++++++++++++ build/package.json | 4 +++- 3 files changed, 39 insertions(+), 2 deletions(-) create mode 100644 build/lib/uses-babel.coffee diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index abeb13f14..d74c0b50e 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -1,6 +1,8 @@ fs = require 'fs' path = require 'path' os = require 'os' +glob = require 'glob' +usesBabel = require './lib/uses-babel' # Add support for obselete APIs of vm module so we can make some third-party # modules work under node v0.11.x. @@ -15,6 +17,7 @@ packageJson = require '../package.json' _.extend(global, require('harmony-collections')) unless global.WeakMap? module.exports = (grunt) -> + grunt.loadNpmTasks('grunt-babel') grunt.loadNpmTasks('grunt-coffeelint') grunt.loadNpmTasks('grunt-lesslint') grunt.loadNpmTasks('grunt-cson') @@ -77,6 +80,11 @@ module.exports = (grunt) -> dest: appDir ext: '.js' + babelConfig = + options: {} + dist: + files: [] + lessConfig = options: paths: [ @@ -141,6 +149,13 @@ module.exports = (grunt) -> pegConfig.glob_to_multiple.src.push("#{directory}/lib/*.pegjs") + for jsFile in glob.sync("#{directory}/lib/**/*.js") + if usesBabel(jsFile) + babelConfig.dist.files.push({ + src: [jsFile] + dest: jsFile + }) + grunt.initConfig pkg: grunt.file.readJSON('package.json') @@ -148,6 +163,8 @@ module.exports = (grunt) -> docsOutputDir: 'docs/output' + babel: babelConfig + coffee: coffeeConfig less: lessConfig @@ -229,7 +246,7 @@ module.exports = (grunt) -> stderr: false failOnError: false - grunt.registerTask('compile', ['coffee', 'prebuild-less', 'cson', 'peg']) + grunt.registerTask('compile', ['babel', 'coffee', 'prebuild-less', 'cson', 'peg']) grunt.registerTask('lint', ['coffeelint', 'csslint', 'lesslint']) grunt.registerTask('test', ['shell:kill-atom', 'run-specs']) diff --git a/build/lib/uses-babel.coffee b/build/lib/uses-babel.coffee new file mode 100644 index 000000000..e7b9050a1 --- /dev/null +++ b/build/lib/uses-babel.coffee @@ -0,0 +1,18 @@ +fs = require 'fs' + +BABEL_PREFIXES = [ + "'use babel'" + '"use babel"' + '/** use babel */' +].map(Buffer) + +PREFIX_LENGTH = Math.max(BABEL_PREFIXES.map((prefix) -> prefix.length)...) + +buffer = Buffer(PREFIX_LENGTH) + +module.exports = (filename) -> + file = fs.openSync(filename, 'r') + fs.readSync(file, buffer, 0, PREFIX_LENGTH) + fs.closeSync(file) + BABEL_PREFIXES.some (prefix) -> + prefix.equals(buffer.slice(0, prefix.length)) diff --git a/build/package.json b/build/package.json index cb41bb0a4..07e9fa7ad 100644 --- a/build/package.json +++ b/build/package.json @@ -12,8 +12,9 @@ "formidable": "~1.0.14", "fs-plus": "2.x", "github-releases": "~0.2.0", + "glob": "^5.0.14", "grunt": "~0.4.1", - "grunt-electron-installer": "^0.37.0", + "grunt-babel": "^5.0.1", "grunt-cli": "~0.1.9", "grunt-coffeelint": "git+https://github.com/atom/grunt-coffeelint.git#cfb99aa99811d52687969532bd5a98011ed95bfe", "grunt-contrib-coffee": "~0.12.0", @@ -21,6 +22,7 @@ "grunt-contrib-less": "~0.8.0", "grunt-cson": "0.14.0", "grunt-download-atom-shell": "~0.14.0", + "grunt-electron-installer": "^0.37.0", "grunt-lesslint": "0.17.0", "grunt-peg": "~1.1.0", "grunt-shell": "~0.3.1", From 76f05814d710ad22c27d1d3c4ee08a40d9e29e82 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 18 Aug 2015 14:51:15 -0700 Subject: [PATCH 03/30] convert-tabs-to-spaces --- build/lib/uses-babel.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build/lib/uses-babel.coffee b/build/lib/uses-babel.coffee index e7b9050a1..e1a7c14ef 100644 --- a/build/lib/uses-babel.coffee +++ b/build/lib/uses-babel.coffee @@ -1,9 +1,9 @@ fs = require 'fs' BABEL_PREFIXES = [ - "'use babel'" - '"use babel"' - '/** use babel */' + "'use babel'" + '"use babel"' + '/** use babel */' ].map(Buffer) PREFIX_LENGTH = Math.max(BABEL_PREFIXES.map((prefix) -> prefix.length)...) From ca58687c889148d9b2f07f2b4cac7b6e884cca3b Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 18 Aug 2015 15:43:38 -0700 Subject: [PATCH 04/30] Write babel-compiled JS files to app dir --- build/Gruntfile.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index d74c0b50e..0ff65d506 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -153,7 +153,7 @@ module.exports = (grunt) -> if usesBabel(jsFile) babelConfig.dist.files.push({ src: [jsFile] - dest: jsFile + dest: path.join(appDir, jsFile) }) grunt.initConfig From e4b1d512cd01175583a615a8493b0ee75ec8f73e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 18 Aug 2015 15:49:34 -0700 Subject: [PATCH 05/30] Honor /** use babel */ in addition to 'use babel' --- spec/babel-spec.coffee | 6 ++++++ spec/fixtures/babel/babel-comment.js | 3 +++ src/babel.coffee | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 spec/fixtures/babel/babel-comment.js diff --git a/spec/babel-spec.coffee b/spec/babel-spec.coffee index 4aec0ef8d..1f71faa93 100644 --- a/spec/babel-spec.coffee +++ b/spec/babel-spec.coffee @@ -33,6 +33,12 @@ describe "Babel transpiler support", -> observedDigest = babel.createBabelVersionAndOptionsDigest(version, defaultOptions) expect(observedDigest).toEqual expectedDigest + describe 'when a .js file starts with /** use babel */;', -> + it "transpiles it using babel", -> + transpiled = require('./fixtures/babel/babel-comment.js') + expect(transpiled(3)).toBe 4 + expect(grim.getDeprecationsLength()).toBe 0 + describe "when a .js file starts with 'use babel';", -> it "transpiles it using babel", -> transpiled = require('./fixtures/babel/babel-single-quotes.js') diff --git a/spec/fixtures/babel/babel-comment.js b/spec/fixtures/babel/babel-comment.js new file mode 100644 index 000000000..4fb454999 --- /dev/null +++ b/spec/fixtures/babel/babel-comment.js @@ -0,0 +1,3 @@ +/** use babel */ + +module.exports = v => v + 1 diff --git a/src/babel.coffee b/src/babel.coffee index 201210817..0adc19db4 100644 --- a/src/babel.coffee +++ b/src/babel.coffee @@ -142,7 +142,7 @@ transpile = (sourceCode, filePath, cachePath) -> # either generated on the fly or pulled from cache. loadFile = (module, filePath) -> sourceCode = fs.readFileSync(filePath, 'utf8') - if sourceCode.startsWith('"use babel"') or sourceCode.startsWith("'use babel'") + if sourceCode.startsWith('"use babel"') or sourceCode.startsWith("'use babel'") or sourceCode.startsWith('/** use babel */') # Continue. else if sourceCode.startsWith('"use 6to5"') or sourceCode.startsWith("'use 6to5'") # Create a manual deprecation since the stack is too deep to use Grim From bea45b5921a3dbd06f083a972b327237d04b9312 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 18 Aug 2015 16:17:27 -0700 Subject: [PATCH 06/30] Don't use Buffer::equals Node 0.10 doesn't have it --- build/lib/uses-babel.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/lib/uses-babel.coffee b/build/lib/uses-babel.coffee index e1a7c14ef..08f8721d2 100644 --- a/build/lib/uses-babel.coffee +++ b/build/lib/uses-babel.coffee @@ -4,7 +4,7 @@ BABEL_PREFIXES = [ "'use babel'" '"use babel"' '/** use babel */' -].map(Buffer) +] PREFIX_LENGTH = Math.max(BABEL_PREFIXES.map((prefix) -> prefix.length)...) @@ -15,4 +15,4 @@ module.exports = (filename) -> fs.readSync(file, buffer, 0, PREFIX_LENGTH) fs.closeSync(file) BABEL_PREFIXES.some (prefix) -> - prefix.equals(buffer.slice(0, prefix.length)) + prefix is buffer.toString('utf8', 0, prefix.length) From a25a8c83e46a9c9c1f741283eb961483ce83577f Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Tue, 18 Aug 2015 17:50:49 -0700 Subject: [PATCH 07/30] Use same babel options for compiling bundled packages --- build/Gruntfile.coffee | 3 ++- src/babel.coffee | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index 0ff65d506..4329c9cd9 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -3,6 +3,7 @@ path = require 'path' os = require 'os' glob = require 'glob' usesBabel = require './lib/uses-babel' +babelOptions = require('../src/babel').defaultOptions # Add support for obselete APIs of vm module so we can make some third-party # modules work under node v0.11.x. @@ -81,7 +82,7 @@ module.exports = (grunt) -> ext: '.js' babelConfig = - options: {} + options: babelOptions dist: files: [] diff --git a/src/babel.coffee b/src/babel.coffee index 0adc19db4..afa48eb1c 100644 --- a/src/babel.coffee +++ b/src/babel.coffee @@ -188,6 +188,7 @@ module.exports = setCacheDirectory: setCacheDirectory getCacheMisses: -> stats.misses getCacheHits: -> stats.hits + defaultOptions: defaultOptions # Visible for testing. createBabelVersionAndOptionsDigest: createBabelVersionAndOptionsDigest From 49e37f541046f0ce9037494d3fe9bf7e85847980 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 19 Aug 2015 16:36:24 -0700 Subject: [PATCH 08/30] Use source-map-support to handle stack trace conversion Previously, our Error.convertStackTrace function was provided by coffeestack, which only works for coffee-script. This adds a dependency on 'source-map-support', which works for any source file with inline source maps. This also refactors the code for registering our compilers (coffee-script, typescript, and babel) so that caching logic is shared. --- package.json | 1 + spec/babel-spec.coffee | 53 +-------- spec/compile-cache-spec.coffee | 39 ------- spec/typescript-spec.coffee | 21 ---- src/babel.coffee | 201 --------------------------------- src/babel.js | 70 ++++++++++++ src/coffee-script.js | 38 +++++++ src/compile-cache.coffee | 30 ----- src/compile-cache.js | 123 ++++++++++++++++++++ src/task.coffee | 10 +- src/typescript.coffee | 106 ----------------- src/typescript.js | 53 +++++++++ static/index.js | 44 +------- 13 files changed, 291 insertions(+), 498 deletions(-) delete mode 100644 spec/compile-cache-spec.coffee delete mode 100644 src/babel.coffee create mode 100644 src/babel.js create mode 100644 src/coffee-script.js delete mode 100644 src/compile-cache.coffee create mode 100644 src/compile-cache.js delete mode 100644 src/typescript.coffee create mode 100644 src/typescript.js diff --git a/package.json b/package.json index 88853e79c..811814a0f 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "semver": "^4.3.3", "serializable": "^1", "service-hub": "^0.6.2", + "source-map-support": "^0.3.2", "space-pen": "3.8.2", "stacktrace-parser": "0.1.1", "temp": "0.8.1", diff --git a/spec/babel-spec.coffee b/spec/babel-spec.coffee index 1f71faa93..421e4c48a 100644 --- a/spec/babel-spec.coffee +++ b/spec/babel-spec.coffee @@ -1,70 +1,19 @@ -babel = require '../src/babel' -crypto = require 'crypto' -grim = require 'grim' - describe "Babel transpiler support", -> - beforeEach -> - jasmine.snapshotDeprecations() - - afterEach -> - jasmine.restoreDeprecationsSnapshot() - - describe "::createBabelVersionAndOptionsDigest", -> - it "returns a digest for the library version and specified options", -> - defaultOptions = - blacklist: [ - 'useStrict' - ] - experimental: true - optional: [ - 'asyncToGenerator' - ] - reactCompat: true - sourceMap: 'inline' - version = '3.0.14' - shasum = crypto.createHash('sha1') - shasum.update('babel-core', 'utf8') - shasum.update('\0', 'utf8') - shasum.update(version, 'utf8') - shasum.update('\0', 'utf8') - shasum.update('{"blacklist": ["useStrict",],"experimental": true,"optional": ["asyncToGenerator",],"reactCompat": true,"sourceMap": "inline",}') - expectedDigest = shasum.digest('hex') - - observedDigest = babel.createBabelVersionAndOptionsDigest(version, defaultOptions) - expect(observedDigest).toEqual expectedDigest - describe 'when a .js file starts with /** use babel */;', -> it "transpiles it using babel", -> transpiled = require('./fixtures/babel/babel-comment.js') expect(transpiled(3)).toBe 4 - expect(grim.getDeprecationsLength()).toBe 0 describe "when a .js file starts with 'use babel';", -> it "transpiles it using babel", -> transpiled = require('./fixtures/babel/babel-single-quotes.js') expect(transpiled(3)).toBe 4 - expect(grim.getDeprecationsLength()).toBe 0 - - describe "when a .js file starts with 'use 6to5';", -> - it "transpiles it using babel and adds a pragma deprecation", -> - expect(grim.getDeprecationsLength()).toBe 0 - transpiled = require('./fixtures/babel/6to5-single-quotes.js') - expect(transpiled(3)).toBe 4 - expect(grim.getDeprecationsLength()).toBe 1 describe 'when a .js file starts with "use babel";', -> it "transpiles it using babel", -> transpiled = require('./fixtures/babel/babel-double-quotes.js') expect(transpiled(3)).toBe 4 - expect(grim.getDeprecationsLength()).toBe 0 - describe 'when a .js file starts with "use 6to5";', -> - it "transpiles it using babel and adds a pragma deprecation", -> - expect(grim.getDeprecationsLength()).toBe 0 - transpiled = require('./fixtures/babel/6to5-double-quotes.js') - expect(transpiled(3)).toBe 4 - expect(grim.getDeprecationsLength()).toBe 1 - - describe "when a .js file does not start with 'use 6to6';", -> + describe "when a .js file does not start with 'use babel';", -> it "does not transpile it using babel", -> expect(-> require('./fixtures/babel/invalid.js')).toThrow() diff --git a/spec/compile-cache-spec.coffee b/spec/compile-cache-spec.coffee deleted file mode 100644 index 6eb6556d0..000000000 --- a/spec/compile-cache-spec.coffee +++ /dev/null @@ -1,39 +0,0 @@ -path = require 'path' -CSON = require 'season' -CoffeeCache = require 'coffee-cash' - -babel = require '../src/babel' -typescript = require '../src/typescript' -CompileCache = require '../src/compile-cache' - -describe "Compile Cache", -> - describe ".addPathToCache(filePath)", -> - it "adds the path to the correct CSON, CoffeeScript, babel or typescript cache", -> - spyOn(CSON, 'readFileSync').andCallThrough() - spyOn(CoffeeCache, 'addPathToCache').andCallThrough() - spyOn(babel, 'addPathToCache').andCallThrough() - spyOn(typescript, 'addPathToCache').andCallThrough() - - CompileCache.addPathToCache(path.join(__dirname, 'fixtures', 'cson.cson')) - expect(CSON.readFileSync.callCount).toBe 1 - expect(CoffeeCache.addPathToCache.callCount).toBe 0 - expect(babel.addPathToCache.callCount).toBe 0 - expect(typescript.addPathToCache.callCount).toBe 0 - - CompileCache.addPathToCache(path.join(__dirname, 'fixtures', 'coffee.coffee')) - expect(CSON.readFileSync.callCount).toBe 1 - expect(CoffeeCache.addPathToCache.callCount).toBe 1 - expect(babel.addPathToCache.callCount).toBe 0 - expect(typescript.addPathToCache.callCount).toBe 0 - - CompileCache.addPathToCache(path.join(__dirname, 'fixtures', 'babel', 'babel-double-quotes.js')) - expect(CSON.readFileSync.callCount).toBe 1 - expect(CoffeeCache.addPathToCache.callCount).toBe 1 - expect(babel.addPathToCache.callCount).toBe 1 - expect(typescript.addPathToCache.callCount).toBe 0 - - CompileCache.addPathToCache(path.join(__dirname, 'fixtures', 'typescript', 'valid.ts')) - expect(CSON.readFileSync.callCount).toBe 1 - expect(CoffeeCache.addPathToCache.callCount).toBe 1 - expect(babel.addPathToCache.callCount).toBe 1 - expect(typescript.addPathToCache.callCount).toBe 1 diff --git a/spec/typescript-spec.coffee b/spec/typescript-spec.coffee index 493715d36..152848c74 100644 --- a/spec/typescript-spec.coffee +++ b/spec/typescript-spec.coffee @@ -1,25 +1,4 @@ -typescript = require '../src/typescript' -crypto = require 'crypto' - describe "TypeScript transpiler support", -> - describe "::createTypeScriptVersionAndOptionsDigest", -> - it "returns a digest for the library version and specified options", -> - defaultOptions = - target: 1 # ES5 - module: 'commonjs' - sourceMap: true - version = '1.4.1' - shasum = crypto.createHash('sha1') - shasum.update('typescript', 'utf8') - shasum.update('\0', 'utf8') - shasum.update(version, 'utf8') - shasum.update('\0', 'utf8') - shasum.update(JSON.stringify(defaultOptions)) - expectedDigest = shasum.digest('hex') - - observedDigest = typescript.createTypeScriptVersionAndOptionsDigest(version, defaultOptions) - expect(observedDigest).toEqual expectedDigest - describe "when there is a .ts file", -> it "transpiles it using typescript", -> transpiled = require('./fixtures/typescript/valid.ts') diff --git a/src/babel.coffee b/src/babel.coffee deleted file mode 100644 index afa48eb1c..000000000 --- a/src/babel.coffee +++ /dev/null @@ -1,201 +0,0 @@ -### -Cache for source code transpiled by Babel. - -Inspired by https://github.com/atom/atom/blob/6b963a562f8d495fbebe6abdbafbc7caf705f2c3/src/coffee-cache.coffee. -### - -crypto = require 'crypto' -fs = require 'fs-plus' -path = require 'path' -babel = null # Defer until used -Grim = null # Defer until used - -stats = - hits: 0 - misses: 0 - -defaultOptions = - # Currently, the cache key is a function of: - # * The version of Babel used to transpile the .js file. - # * The contents of this defaultOptions object. - # * The contents of the .js file. - # That means that we cannot allow information from an unknown source - # to affect the cache key for the output of transpilation, which means - # we cannot allow users to override these default options via a .babelrc - # file, because the contents of that .babelrc file will not make it into - # the cache key. It would be great to support .babelrc files once we - # have a way to do so that is safe with respect to caching. - breakConfig: true - - # The Chrome dev tools will show the original version of the file - # when the source map is inlined. - sourceMap: 'inline' - - # Blacklisted features do not get transpiled. Features that are - # natively supported in the target environment should be listed - # here. Because Atom uses a bleeding edge version of Node/io.js, - # I think this can include es6.arrowFunctions, es6.classes, and - # possibly others, but I want to be conservative. - blacklist: [ - 'es6.forOf' - 'useStrict' - ] - - optional: [ - # Target a version of the regenerator runtime that - # supports yield so the transpiled code is cleaner/smaller. - 'asyncToGenerator' - ] - - # Includes support for es7 features listed at: - # http://babeljs.io/docs/usage/experimental/. - stage: 0 - - -### -shasum - Hash with an update() method. -value - Must be a value that could be returned by JSON.parse(). -### -updateDigestForJsonValue = (shasum, value) -> - # Implmentation is similar to that of pretty-printing a JSON object, except: - # * Strings are not escaped. - # * No effort is made to avoid trailing commas. - # These shortcuts should not affect the correctness of this function. - type = typeof value - if type is 'string' - shasum.update('"', 'utf8') - shasum.update(value, 'utf8') - shasum.update('"', 'utf8') - else if type in ['boolean', 'number'] - shasum.update(value.toString(), 'utf8') - else if value is null - shasum.update('null', 'utf8') - else if Array.isArray value - shasum.update('[', 'utf8') - for item in value - updateDigestForJsonValue(shasum, item) - shasum.update(',', 'utf8') - shasum.update(']', 'utf8') - else - # value must be an object: be sure to sort the keys. - keys = Object.keys value - keys.sort() - - shasum.update('{', 'utf8') - for key in keys - updateDigestForJsonValue(shasum, key) - shasum.update(': ', 'utf8') - updateDigestForJsonValue(shasum, value[key]) - shasum.update(',', 'utf8') - shasum.update('}', 'utf8') - -createBabelVersionAndOptionsDigest = (version, options) -> - shasum = crypto.createHash('sha1') - # Include the version of babel in the hash. - shasum.update('babel-core', 'utf8') - shasum.update('\0', 'utf8') - shasum.update(version, 'utf8') - shasum.update('\0', 'utf8') - updateDigestForJsonValue(shasum, options) - shasum.digest('hex') - -cacheDir = null -jsCacheDir = null - -getCachePath = (sourceCode) -> - digest = crypto.createHash('sha1').update(sourceCode, 'utf8').digest('hex') - - unless jsCacheDir? - to5Version = require('babel-core/package.json').version - jsCacheDir = path.join(cacheDir, createBabelVersionAndOptionsDigest(to5Version, defaultOptions)) - - path.join(jsCacheDir, "#{digest}.js") - -getCachedJavaScript = (cachePath) -> - if fs.isFileSync(cachePath) - try - cachedJavaScript = fs.readFileSync(cachePath, 'utf8') - stats.hits++ - return cachedJavaScript - null - -# Returns the babel options that should be used to transpile filePath. -createOptions = (filePath) -> - options = filename: filePath - for key, value of defaultOptions - options[key] = value - options - -transpile = (sourceCode, filePath, cachePath) -> - options = createOptions(filePath) - babel ?= require 'babel-core' - js = babel.transform(sourceCode, options).code - stats.misses++ - - try - fs.writeFileSync(cachePath, js) - - js - -# Function that obeys the contract of an entry in the require.extensions map. -# Returns the transpiled version of the JavaScript code at filePath, which is -# either generated on the fly or pulled from cache. -loadFile = (module, filePath) -> - sourceCode = fs.readFileSync(filePath, 'utf8') - if sourceCode.startsWith('"use babel"') or sourceCode.startsWith("'use babel'") or sourceCode.startsWith('/** use babel */') - # Continue. - else if sourceCode.startsWith('"use 6to5"') or sourceCode.startsWith("'use 6to5'") - # Create a manual deprecation since the stack is too deep to use Grim - # which limits the depth to 3 - Grim ?= require 'grim' - stack = [ - { - fileName: __filename - functionName: 'loadFile' - location: "#{__filename}:161:5" - } - { - fileName: filePath - functionName: '' - location: "#{filePath}:1:1" - } - ] - deprecation = - message: "Use the 'use babel' pragma instead of 'use 6to5'" - stacks: [stack] - Grim.addSerializedDeprecation(deprecation) - else - return module._compile(sourceCode, filePath) - - cachePath = getCachePath(sourceCode) - js = getCachedJavaScript(cachePath) ? transpile(sourceCode, filePath, cachePath) - module._compile(js, filePath) - -register = -> - Object.defineProperty(require.extensions, '.js', { - enumerable: true - writable: false - value: loadFile - }) - -setCacheDirectory = (newCacheDir) -> - if cacheDir isnt newCacheDir - cacheDir = newCacheDir - jsCacheDir = null - -module.exports = - register: register - setCacheDirectory: setCacheDirectory - getCacheMisses: -> stats.misses - getCacheHits: -> stats.hits - defaultOptions: defaultOptions - - # Visible for testing. - createBabelVersionAndOptionsDigest: createBabelVersionAndOptionsDigest - - addPathToCache: (filePath) -> - return if path.extname(filePath) isnt '.js' - - sourceCode = fs.readFileSync(filePath, 'utf8') - cachePath = getCachePath(sourceCode) - transpile(sourceCode, filePath, cachePath) diff --git a/src/babel.js b/src/babel.js new file mode 100644 index 000000000..eedfef5e2 --- /dev/null +++ b/src/babel.js @@ -0,0 +1,70 @@ +'use strict' + +const _ = require('underscore-plus') +const crypto = require('crypto') +const path = require('path') + +let babel = null +let babelVersionDirectory = null + +// This field is used by the Gruntfile for compiling babel in bundled packages. +exports.defaultOptions = { + + // Currently, the cache key is a function of: + // * The version of Babel used to transpile the .js file. + // * The contents of this defaultOptions object. + // * The contents of the .js file. + // That means that we cannot allow information from an unknown source + // to affect the cache key for the output of transpilation, which means + // we cannot allow users to override these default options via a .babelrc + // file, because the contents of that .babelrc file will not make it into + // the cache key. It would be great to support .babelrc files once we + // have a way to do so that is safe with respect to caching. + breakConfig: true, + + sourceMap: 'inline', + blacklist: ['es6.forOf', 'useStrict'], + optional: ['asyncToGenerator'], + stage: 0 +} + +exports.shouldCompile = function(sourceCode) { + return sourceCode.startsWith('/** use babel */') || + sourceCode.startsWith('"use babel"') || + sourceCode.startsWith("'use babel'") +} + +exports.getCachePath = function(sourceCode) { + if (babelVersionDirectory == null) { + let babelVersion = require('babel-core/package.json').version + babelVersionDirectory = path.join('js', 'babel', createVersionAndOptionsDigest(babelVersion, defaultOptions)) + } + + return path.join( + babelVersionDirectory, + crypto + .createHash('sha1') + .update(sourceCode, 'utf8') + .digest('hex') + ".js" + ) +} + +exports.compile = function(sourceCode, filePath) { + if (!babel) { + babel = require('babel-core') + } + + let options = _.defaults({filename: filePath}, defaultOptions) + return babel.transform(sourceCode, options).code +} + +function createVersionAndOptionsDigest (version, options) { + return crypto + .createHash('sha1') + .update('babel-core', 'utf8') + .update('\0', 'utf8') + .update(version, 'utf8') + .update('\0', 'utf8') + .update(JSON.stringify(options), 'utf8') + .digest('hex') +} diff --git a/src/coffee-script.js b/src/coffee-script.js new file mode 100644 index 000000000..9cd0a0777 --- /dev/null +++ b/src/coffee-script.js @@ -0,0 +1,38 @@ +'use strict' + +const crypto = require('crypto') +const path = require('path') + +// The coffee-script compiler is required eagerly because: +// 1. It is always used. +// 2. It reassigns Error.prepareStackTrace, so we need to make sure that +// the 'source-map-support' module is installed *after* it is loaded. +const CoffeeScript = require('coffee-script') + +exports.shouldCompile = function() { + return true +} + +exports.getCachePath = function(sourceCode) { + return path.join( + "coffee", + crypto + .createHash('sha1') + .update(sourceCode, 'utf8') + .digest('hex') + ".js" + ) +} + +exports.compile = function(sourceCode, filePath) { + let output = CoffeeScript.compile(sourceCode, { + filename: filePath, + sourceMap: true + }) + + let js = output.js + js += '\n' + js += '//# sourceMappingURL=data:application/json;base64,' + js += new Buffer(output.v3SourceMap).toString('base64') + js += '\n' + return js +} diff --git a/src/compile-cache.coffee b/src/compile-cache.coffee deleted file mode 100644 index 8fe8d6711..000000000 --- a/src/compile-cache.coffee +++ /dev/null @@ -1,30 +0,0 @@ -path = require 'path' -CSON = require 'season' -CoffeeCache = require 'coffee-cash' -babel = require './babel' -typescript = require './typescript' - -# This file is required directly by apm so that files can be cached during -# package install so that the first package load in Atom doesn't have to -# compile anything. -exports.addPathToCache = (filePath, atomHome) -> - atomHome ?= process.env.ATOM_HOME - cacheDir = path.join(atomHome, 'compile-cache') - # Use separate compile cache when sudo'ing as root to avoid permission issues - if process.env.USER is 'root' and process.env.SUDO_USER and process.env.SUDO_USER isnt process.env.USER - cacheDir = path.join(cacheDir, 'root') - - CoffeeCache.setCacheDirectory(path.join(cacheDir, 'coffee')) - CSON.setCacheDir(path.join(cacheDir, 'cson')) - babel.setCacheDirectory(path.join(cacheDir, 'js', 'babel')) - typescript.setCacheDirectory(path.join(cacheDir, 'ts')) - - switch path.extname(filePath) - when '.coffee' - CoffeeCache.addPathToCache(filePath) - when '.cson' - CSON.readFileSync(filePath) - when '.js' - babel.addPathToCache(filePath) - when '.ts' - typescript.addPathToCache(filePath) diff --git a/src/compile-cache.js b/src/compile-cache.js new file mode 100644 index 000000000..0666e7635 --- /dev/null +++ b/src/compile-cache.js @@ -0,0 +1,123 @@ +'use strict' + +const path = require('path') +const CSON = require('season') +const fs = require('fs-plus') + +const COMPILERS = { + '.js': require('./babel'), + '.ts': require('./typescript'), + '.coffee': require('./coffee-script') +} + +for (let extension in COMPILERS) { + let compiler = COMPILERS[extension] + Object.defineProperty(require.extensions, extension, { + enumerable: true, + writable: false, + value: function (module, filePath) { + let code = compileFileAtPath(compiler, filePath) + return module._compile(code, filePath) + } + }) +} + +let cacheDirectory = null + +exports.setAtomHomeDirectory = function (atomHome) { + let cacheDir = path.join(atomHome, 'compile-cache') + if (process.env.USER === 'root' && process.env.SUDO_USER && process.env.SUDO_USER !== process.env.USER) { + cacheDir = path.join(cacheDirectory, 'root') + } + this.setCacheDirectory(cacheDir) +} + +exports.setCacheDirectory = function (directory) { + cacheDirectory = directory + CSON.setCacheDir(path.join(cacheDirectory, 'cson')); +} + +exports.getCacheDirectory = function () { + return cacheDirectory +} + +exports.addPathToCache = function (filePath, atomHome) { + this.setAtomHomeDirectory(atomHome) + extension = path.extname(filePath) + if (extension === '.cson') { + return CSON.readFileSync(filePath) + } + + if (compiler = COMPILERS[extension]) { + return compileFileAtPath(compiler, filePath) + } +} + +function compileFileAtPath (compiler, filePath) { + let sourceCode = fs.readFileSync(filePath, 'utf8') + if (compiler.shouldCompile(sourceCode, filePath)) { + let cachePath = compiler.getCachePath(sourceCode, filePath) + let compiledCode = readCachedJavascript(cachePath) + if (compiledCode == null) { + compiledCode = compiler.compile(sourceCode, filePath) + writeCachedJavascript(cachePath, compiledCode) + } + return compiledCode + } + return sourceCode +} + +function readCachedJavascript (relativeCachePath) { + let cachePath = path.join(cacheDirectory, relativeCachePath) + if (fs.isFileSync(cachePath)) { + try { + return fs.readFileSync(cachePath, 'utf8') + } catch (error) {} + } + return null +} + +function writeCachedJavascript (relativeCachePath, code) { + let cachePath = path.join(cacheDirectory, relativeCachePath) + fs.writeFileSync(cachePath, code, 'utf8') +} + +const InlineSourceMapRegExp = /\/\/[#@]\s*sourceMappingURL=([^'"]+)\s*$/g + +require('source-map-support').install({ + handleUncaughtExceptions: false, + + // Most of this logic is the same as the default implementation in the + // source-map-support module, but we've overridden it to read the javascript + // code from our cache directory. + retrieveSourceMap: function (filePath) { + if (!fs.isFileSync(filePath)){ + return null + } + + let sourceCode = fs.readFileSync(filePath, 'utf8') + let compiler = COMPILERS[path.extname(filePath)] + let fileData = readCachedJavascript(compiler.getCachePath(sourceCode, filePath)) + if (fileData == null) { + return null + } + + let match, lastMatch + InlineSourceMapRegExp.lastIndex = 0 + while ((match = InlineSourceMapRegExp.exec(fileData))) { + lastMatch = match + } + if (lastMatch == null){ + return null + } + + let sourceMappingURL = lastMatch[1] + let rawData = sourceMappingURL.slice(sourceMappingURL.indexOf(',') + 1) + let sourceMapData = new Buffer(rawData, 'base64').toString() + + return { + map: JSON.parse(sourceMapData), + url: null + } + } +}) diff --git a/src/task.coffee b/src/task.coffee index 337192dcd..6b1162396 100644 --- a/src/task.coffee +++ b/src/task.coffee @@ -66,15 +66,11 @@ class Task # * `taskPath` The {String} path to the CoffeeScript/JavaScript file that # exports a single {Function} to execute. constructor: (taskPath) -> - coffeeCacheRequire = "require('#{require.resolve('coffee-cash')}')" - coffeeCachePath = require('coffee-cash').getCacheDirectory() - coffeeStackRequire = "require('#{require.resolve('coffeestack')}')" - stackCachePath = require('coffeestack').getCacheDirectory() + compileCacheRequire = "require('#{require.resolve('./compile-cache')}')" + compileCachePath = require('./compile-cache').getCacheDirectory() taskBootstrapRequire = "require('#{require.resolve('./task-bootstrap')}');" bootstrap = """ - #{coffeeCacheRequire}.setCacheDirectory('#{coffeeCachePath}'); - #{coffeeCacheRequire}.register(); - #{coffeeStackRequire}.setCacheDirectory('#{stackCachePath}'); + #{compileCacheRequire}.setCacheDirectory('#{compileCachePath}'); #{taskBootstrapRequire} """ bootstrap = bootstrap.replace(/\\/g, "\\\\") diff --git a/src/typescript.coffee b/src/typescript.coffee deleted file mode 100644 index 3a54941f3..000000000 --- a/src/typescript.coffee +++ /dev/null @@ -1,106 +0,0 @@ -### -Cache for source code transpiled by TypeScript. - -Inspired by https://github.com/atom/atom/blob/7a719d585db96ff7d2977db9067e1d9d4d0adf1a/src/babel.coffee -### - -crypto = require 'crypto' -fs = require 'fs-plus' -path = require 'path' -tss = null # Defer until used - -stats = - hits: 0 - misses: 0 - -defaultOptions = - target: 1 # ES5 - module: 'commonjs' - sourceMap: true - -createTypeScriptVersionAndOptionsDigest = (version, options) -> - shasum = crypto.createHash('sha1') - # Include the version of typescript in the hash. - shasum.update('typescript', 'utf8') - shasum.update('\0', 'utf8') - shasum.update(version, 'utf8') - shasum.update('\0', 'utf8') - shasum.update(JSON.stringify(options)) - shasum.digest('hex') - -cacheDir = null -jsCacheDir = null - -getCachePath = (sourceCode) -> - digest = crypto.createHash('sha1').update(sourceCode, 'utf8').digest('hex') - - unless jsCacheDir? - tssVersion = require('typescript-simple/package.json').version - jsCacheDir = path.join(cacheDir, createTypeScriptVersionAndOptionsDigest(tssVersion, defaultOptions)) - - path.join(jsCacheDir, "#{digest}.js") - -getCachedJavaScript = (cachePath) -> - if fs.isFileSync(cachePath) - try - cachedJavaScript = fs.readFileSync(cachePath, 'utf8') - stats.hits++ - return cachedJavaScript - null - -# Returns the TypeScript options that should be used to transpile filePath. -createOptions = (filePath) -> - options = filename: filePath - for key, value of defaultOptions - options[key] = value - options - -transpile = (sourceCode, filePath, cachePath) -> - options = createOptions(filePath) - unless tss? - {TypeScriptSimple} = require 'typescript-simple' - tss = new TypeScriptSimple(options, false) - js = tss.compile(sourceCode, filePath) - stats.misses++ - - try - fs.writeFileSync(cachePath, js) - - js - -# Function that obeys the contract of an entry in the require.extensions map. -# Returns the transpiled version of the JavaScript code at filePath, which is -# either generated on the fly or pulled from cache. -loadFile = (module, filePath) -> - sourceCode = fs.readFileSync(filePath, 'utf8') - cachePath = getCachePath(sourceCode) - js = getCachedJavaScript(cachePath) ? transpile(sourceCode, filePath, cachePath) - module._compile(js, filePath) - -register = -> - Object.defineProperty(require.extensions, '.ts', { - enumerable: true - writable: false - value: loadFile - }) - -setCacheDirectory = (newCacheDir) -> - if cacheDir isnt newCacheDir - cacheDir = newCacheDir - jsCacheDir = null - -module.exports = - register: register - setCacheDirectory: setCacheDirectory - getCacheMisses: -> stats.misses - getCacheHits: -> stats.hits - - # Visible for testing. - createTypeScriptVersionAndOptionsDigest: createTypeScriptVersionAndOptionsDigest - - addPathToCache: (filePath) -> - return if path.extname(filePath) isnt '.ts' - - sourceCode = fs.readFileSync(filePath, 'utf8') - cachePath = getCachePath(sourceCode) - transpile(sourceCode, filePath, cachePath) diff --git a/src/typescript.js b/src/typescript.js new file mode 100644 index 000000000..c372c775b --- /dev/null +++ b/src/typescript.js @@ -0,0 +1,53 @@ +'use strict' + +const _ = require('underscore-plus') +const crypto = require('crypto') +const path = require('path') + +let TypeScriptSimple = null +let typescriptVersionDir = null + +const defaultOptions = { + target: 1, + module: 'commonjs', + sourceMap: true +} + +exports.shouldCompile = function() { + return true +} + +exports.getCachePath = function(sourceCode) { + if (typescriptVersionDir == null) { + let version = require('typescript-simple/package.json').version + typescriptVersionDir = path.join('ts', createVersionAndOptionsDigest(version, defaultOptions)) + } + + return path.join( + typescriptVersionDir, + crypto + .createHash('sha1') + .update(sourceCode, 'utf8') + .digest('hex') + ".js" + ) +} + +exports.compile = function(sourceCode, filePath) { + if (!TypeScriptSimple) { + TypeScriptSimple = require('typescript-simple').TypeScriptSimple + } + + let options = _.defaults({filename: filePath}, defaultOptions) + return new TypeScriptSimple(options, false).compile(sourceCode, filePath) +} + +function createVersionAndOptionsDigest (version, options) { + return crypto + .createHash('sha1') + .update('typescript', 'utf8') + .update('\0', 'utf8') + .update(version, 'utf8') + .update('\0', 'utf8') + .update(JSON.stringify(options), 'utf8') + .digest('hex') +} diff --git a/static/index.js b/static/index.js index 8fe71a6a9..238cb385e 100644 --- a/static/index.js +++ b/static/index.js @@ -41,15 +41,6 @@ window.onload = function() { } } -var getCacheDirectory = function() { - var cacheDir = path.join(process.env.ATOM_HOME, 'compile-cache'); - // Use separate compile cache when sudo'ing as root to avoid permission issues - if (process.env.USER === 'root' && process.env.SUDO_USER && process.env.SUDO_USER !== process.env.USER) { - cacheDir = path.join(cacheDir, 'root'); - } - return cacheDir; -} - var setLoadTime = function(loadTime) { if (global.atom) { global.atom.loadTime = loadTime; @@ -67,9 +58,8 @@ var handleSetupError = function(error) { } var setupWindow = function(loadSettings) { - var cacheDir = getCacheDirectory(); - - setupCoffeeCache(cacheDir); + var compileCache = require('../src/compile-cache') + compileCache.setAtomHomeDirectory(process.env.ATOM_HOME) ModuleCache = require('../src/module-cache'); ModuleCache.register(loadSettings); @@ -88,21 +78,11 @@ var setupWindow = function(loadSettings) { }); setupVmCompatibility(); - setupCsonCache(cacheDir); - setupSourceMapCache(cacheDir); - setupBabel(cacheDir); - setupTypeScript(cacheDir); require(loadSettings.bootstrapScript); require('ipc').sendChannel('window-command', 'window:loaded'); } -var setupCoffeeCache = function(cacheDir) { - var CoffeeCache = require('coffee-cash'); - CoffeeCache.setCacheDirectory(path.join(cacheDir, 'coffee')); - CoffeeCache.register(); -} - var setupAtomHome = function() { if (!process.env.ATOM_HOME) { var home; @@ -121,26 +101,6 @@ var setupAtomHome = function() { } } -var setupBabel = function(cacheDir) { - var babel = require('../src/babel'); - babel.setCacheDirectory(path.join(cacheDir, 'js', 'babel')); - babel.register(); -} - -var setupTypeScript = function(cacheDir) { - var typescript = require('../src/typescript'); - typescript.setCacheDirectory(path.join(cacheDir, 'typescript')); - typescript.register(); -} - -var setupCsonCache = function(cacheDir) { - require('season').setCacheDir(path.join(cacheDir, 'cson')); -} - -var setupSourceMapCache = function(cacheDir) { - require('coffeestack').setCacheDirectory(path.join(cacheDir, 'coffee', 'source-maps')); -} - var setupVmCompatibility = function() { var vm = require('vm'); if (!vm.Script.createContext) { From 2285e8a42c3f12d47d31c22f96c541ca48a575ee Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Wed, 19 Aug 2015 17:27:57 -0700 Subject: [PATCH 09/30] Move babel config to a separate file in static directory --- build/Gruntfile.coffee | 2 +- src/babel.js | 21 +-------------------- static/babelrc.json | 7 +++++++ 3 files changed, 9 insertions(+), 21 deletions(-) create mode 100644 static/babelrc.json diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index 4329c9cd9..860616745 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -3,7 +3,7 @@ path = require 'path' os = require 'os' glob = require 'glob' usesBabel = require './lib/uses-babel' -babelOptions = require('../src/babel').defaultOptions +babelOptions = require('../static/babelrc.json') # Add support for obselete APIs of vm module so we can make some third-party # modules work under node v0.11.x. diff --git a/src/babel.js b/src/babel.js index eedfef5e2..a80694587 100644 --- a/src/babel.js +++ b/src/babel.js @@ -7,26 +7,7 @@ const path = require('path') let babel = null let babelVersionDirectory = null -// This field is used by the Gruntfile for compiling babel in bundled packages. -exports.defaultOptions = { - - // Currently, the cache key is a function of: - // * The version of Babel used to transpile the .js file. - // * The contents of this defaultOptions object. - // * The contents of the .js file. - // That means that we cannot allow information from an unknown source - // to affect the cache key for the output of transpilation, which means - // we cannot allow users to override these default options via a .babelrc - // file, because the contents of that .babelrc file will not make it into - // the cache key. It would be great to support .babelrc files once we - // have a way to do so that is safe with respect to caching. - breakConfig: true, - - sourceMap: 'inline', - blacklist: ['es6.forOf', 'useStrict'], - optional: ['asyncToGenerator'], - stage: 0 -} +const defaultOptions = require('../static/babelrc.json') exports.shouldCompile = function(sourceCode) { return sourceCode.startsWith('/** use babel */') || diff --git a/static/babelrc.json b/static/babelrc.json new file mode 100644 index 000000000..26b70dc41 --- /dev/null +++ b/static/babelrc.json @@ -0,0 +1,7 @@ +{ + "breakConfig": true, + "sourceMap": "inline", + "blacklist": ["es6.forOf", "useStrict"], + "optional": ["asyncToGenerator"], + "stage": 0 +} From 1bfbf7f9d30c6722241639c15c4f74fc4aa949b7 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 20 Aug 2015 10:00:38 -0700 Subject: [PATCH 10/30] Map line numbers in onerror handler w/ source-map-support --- package.json | 1 - spec/atom-reporter.coffee | 4 +--- src/atom.coffee | 8 ++------ 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 811814a0f..3d9061beb 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,6 @@ "clear-cut": "^2.0.1", "coffee-cash": "0.8.0", "coffee-script": "1.8.0", - "coffeestack": "^1.1.2", "color": "^0.7.3", "delegato": "^1", "emissary": "^1.3.3", diff --git a/spec/atom-reporter.coffee b/spec/atom-reporter.coffee index 1fdbd1385..5cd700c39 100644 --- a/spec/atom-reporter.coffee +++ b/spec/atom-reporter.coffee @@ -1,11 +1,9 @@ path = require 'path' _ = require 'underscore-plus' -{convertStackTrace} = require 'coffeestack' {View, $, $$} = require '../src/space-pen-extensions' grim = require 'grim' marked = require 'marked' -sourceMaps = {} formatStackTrace = (spec, message='', stackTrace) -> return stackTrace unless stackTrace @@ -15,8 +13,8 @@ formatStackTrace = (spec, message='', stackTrace) -> for line in stackTrace.split('\n') convertedLines.push(line) unless jasminePattern.test(line) break if firstJasmineLinePattern.test(line) + stackTrace = convertedLines.join('\n') - stackTrace = convertStackTrace(convertedLines.join('\n'), sourceMaps) lines = stackTrace.split('\n') # Remove first line of stack when it is the same as the error message diff --git a/src/atom.coffee b/src/atom.coffee index 7065a0d93..28058f8dd 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -9,7 +9,7 @@ _ = require 'underscore-plus' {deprecate, includeDeprecatedAPIs} = require 'grim' {CompositeDisposable, Emitter} = require 'event-kit' fs = require 'fs-plus' -{convertStackTrace, convertLine} = require 'coffeestack' +{mapSourcePosition} = require 'source-map-support' Model = require './model' {$} = require './space-pen-extensions' WindowEventHandler = require './window-event-handler' @@ -196,15 +196,11 @@ class Atom extends Model # # Call after this instance has been assigned to the `atom` global. initialize: -> - sourceMapCache = {} - window.onerror = => @lastUncaughtError = Array::slice.call(arguments) [message, url, line, column, originalError] = @lastUncaughtError - convertedLine = convertLine(url, line, column, sourceMapCache) - {line, column} = convertedLine if convertedLine? - originalError.stack = convertStackTrace(originalError.stack, sourceMapCache) if originalError + {line, column} = mapSourcePosition({source: url, line, column}) eventObject = {message, url, line, column, originalError} From 973c2a6a18f7e7229fa73d1b80456c69a13e63e4 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 20 Aug 2015 11:22:32 -0700 Subject: [PATCH 11/30] Don't match newlines when finding source-map data --- src/compile-cache.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compile-cache.js b/src/compile-cache.js index 0666e7635..61a703abe 100644 --- a/src/compile-cache.js +++ b/src/compile-cache.js @@ -82,7 +82,7 @@ function writeCachedJavascript (relativeCachePath, code) { fs.writeFileSync(cachePath, code, 'utf8') } -const InlineSourceMapRegExp = /\/\/[#@]\s*sourceMappingURL=([^'"]+)\s*$/g +const InlineSourceMapRegExp = /\/\/[#@]\s*sourceMappingURL=([^'"\n]+)\s*$/g require('source-map-support').install({ handleUncaughtExceptions: false, From 070f4000f0039b8cc9fbbff2cfccaa8b028e6c81 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 20 Aug 2015 11:25:38 -0700 Subject: [PATCH 12/30] Replace 'use babel' -> '@babel' in comment directive --- build/lib/uses-babel.coffee | 2 +- spec/babel-spec.coffee | 2 +- spec/fixtures/babel/babel-comment.js | 2 +- src/babel.js | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/lib/uses-babel.coffee b/build/lib/uses-babel.coffee index 08f8721d2..dcd480d4e 100644 --- a/build/lib/uses-babel.coffee +++ b/build/lib/uses-babel.coffee @@ -3,7 +3,7 @@ fs = require 'fs' BABEL_PREFIXES = [ "'use babel'" '"use babel"' - '/** use babel */' + '/** @babel */' ] PREFIX_LENGTH = Math.max(BABEL_PREFIXES.map((prefix) -> prefix.length)...) diff --git a/spec/babel-spec.coffee b/spec/babel-spec.coffee index 421e4c48a..caaaed9f2 100644 --- a/spec/babel-spec.coffee +++ b/spec/babel-spec.coffee @@ -1,5 +1,5 @@ describe "Babel transpiler support", -> - describe 'when a .js file starts with /** use babel */;', -> + describe 'when a .js file starts with /** @babel */;', -> it "transpiles it using babel", -> transpiled = require('./fixtures/babel/babel-comment.js') expect(transpiled(3)).toBe 4 diff --git a/spec/fixtures/babel/babel-comment.js b/spec/fixtures/babel/babel-comment.js index 4fb454999..ee9a27373 100644 --- a/spec/fixtures/babel/babel-comment.js +++ b/spec/fixtures/babel/babel-comment.js @@ -1,3 +1,3 @@ -/** use babel */ +/** @babel */ module.exports = v => v + 1 diff --git a/src/babel.js b/src/babel.js index a80694587..738433ed8 100644 --- a/src/babel.js +++ b/src/babel.js @@ -10,7 +10,7 @@ let babelVersionDirectory = null const defaultOptions = require('../static/babelrc.json') exports.shouldCompile = function(sourceCode) { - return sourceCode.startsWith('/** use babel */') || + return sourceCode.startsWith('/** @babel */') || sourceCode.startsWith('"use babel"') || sourceCode.startsWith("'use babel'") } From 6ef15468eb9b60c833f34fb08f2c30f46015b9ef Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 20 Aug 2015 13:35:49 -0600 Subject: [PATCH 13/30] :arrow_up: grim to drop its custom CoffeeScript stack trace support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It’s interfering with our global stack trace support. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3d9061beb..b3527e292 100644 --- a/package.json +++ b/package.json @@ -31,7 +31,7 @@ "fstream": "0.1.24", "fuzzaldrin": "^2.1", "git-utils": "^3.0.0", - "grim": "1.4.1", + "grim": "1.4.2", "jasmine-json": "~0.0", "jasmine-tagged": "^1.1.4", "jquery": "^2.1.1", From 61ef1da81531839efb281278e438318ac2a61859 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 20 Aug 2015 16:07:37 -0600 Subject: [PATCH 14/30] :art: Signed-off-by: Max Brunsfeld --- src/compile-cache.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compile-cache.js b/src/compile-cache.js index 61a703abe..cea85cbe9 100644 --- a/src/compile-cache.js +++ b/src/compile-cache.js @@ -113,10 +113,10 @@ require('source-map-support').install({ let sourceMappingURL = lastMatch[1] let rawData = sourceMappingURL.slice(sourceMappingURL.indexOf(',') + 1) - let sourceMapData = new Buffer(rawData, 'base64').toString() + let sourceMap = JSON.parse(new Buffer(rawData, 'base64').toString()) return { - map: JSON.parse(sourceMapData), + map: sourceMap, url: null } } From c0f3ca6dcdc56c45582c86249665b66f5c45784b Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 20 Aug 2015 16:08:03 -0600 Subject: [PATCH 15/30] Fix inline source map regex Need to enable multi-line mode after all Signed-off-by: Max Brunsfeld --- src/compile-cache.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compile-cache.js b/src/compile-cache.js index cea85cbe9..821eac45e 100644 --- a/src/compile-cache.js +++ b/src/compile-cache.js @@ -82,7 +82,7 @@ function writeCachedJavascript (relativeCachePath, code) { fs.writeFileSync(cachePath, code, 'utf8') } -const InlineSourceMapRegExp = /\/\/[#@]\s*sourceMappingURL=([^'"\n]+)\s*$/g +const INLINE_SOURCE_MAP_REGEXP = /\/\/[#@]\s*sourceMappingURL=([^'"\n]+)\s*$/mg require('source-map-support').install({ handleUncaughtExceptions: false, @@ -103,8 +103,8 @@ require('source-map-support').install({ } let match, lastMatch - InlineSourceMapRegExp.lastIndex = 0 - while ((match = InlineSourceMapRegExp.exec(fileData))) { + INLINE_SOURCE_MAP_REGEXP.lastIndex = 0 + while ((match = INLINE_SOURCE_MAP_REGEXP.exec(fileData))) { lastMatch = match } if (lastMatch == null){ From 6f44fc4c7f7a0b85ce483b1b641493c4bdbc74e8 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Thu, 20 Aug 2015 16:08:25 -0600 Subject: [PATCH 16/30] Generate sourceFiles in CoffeeScript source maps correctly --- src/coffee-script.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coffee-script.js b/src/coffee-script.js index 9cd0a0777..0aa428ab8 100644 --- a/src/coffee-script.js +++ b/src/coffee-script.js @@ -26,6 +26,7 @@ exports.getCachePath = function(sourceCode) { exports.compile = function(sourceCode, filePath) { let output = CoffeeScript.compile(sourceCode, { filename: filePath, + sourceFiles: [filePath], sourceMap: true }) From f531684acf7ee03eda4bba64e3a7be71433c2dc9 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Thu, 20 Aug 2015 17:59:29 -0700 Subject: [PATCH 17/30] Use compile-cache in browser process (for dev mode) --- package.json | 1 - src/browser/main.coffee | 13 ++++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 5cf309976..b80369c35 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,6 @@ "babel-core": "^5.8.21", "bootstrap": "^3.3.4", "clear-cut": "^2.0.1", - "coffee-cash": "0.8.0", "coffee-script": "1.8.0", "color": "^0.7.3", "delegato": "^1", diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 8d429f15f..fac1191a7 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -16,7 +16,7 @@ process.on 'uncaughtException', (error={}) -> start = -> setupAtomHome() - setupCoffeeCache() + setupCompileCache() if process.platform is 'win32' SquirrelUpdate = require './squirrel-update' @@ -77,14 +77,9 @@ setupAtomHome = -> atomHome = fs.realpathSync(atomHome) process.env.ATOM_HOME = atomHome -setupCoffeeCache = -> - CoffeeCache = require 'coffee-cash' - cacheDir = path.join(process.env.ATOM_HOME, 'compile-cache') - # Use separate compile cache when sudo'ing as root to avoid permission issues - if process.env.USER is 'root' and process.env.SUDO_USER and process.env.SUDO_USER isnt process.env.USER - cacheDir = path.join(cacheDir, 'root') - CoffeeCache.setCacheDirectory(path.join(cacheDir, 'coffee')) - CoffeeCache.register() +setupCompileCache = -> + compileCache = require('../compile-cache') + compileCache.setAtomHomeDirectory(process.env.ATOM_HOME) parseCommandLine = -> version = app.getVersion() From 8c5eed28d20fee1d64ad647e2ca45dd2c4712ee3 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 21 Aug 2015 10:31:29 -0700 Subject: [PATCH 18/30] Don't use es6 features in compile-cache APM requires it directly, and it uses an older version of node --- src/babel.js | 32 ++++++++++++++-------- src/coffee-script.js | 10 +++---- src/compile-cache.js | 64 +++++++++++++++++++++++--------------------- src/typescript.js | 18 ++++++------- 4 files changed, 68 insertions(+), 56 deletions(-) diff --git a/src/babel.js b/src/babel.js index 738433ed8..e44d78996 100644 --- a/src/babel.js +++ b/src/babel.js @@ -1,23 +1,33 @@ 'use strict' -const _ = require('underscore-plus') -const crypto = require('crypto') -const path = require('path') +var _ = require('underscore-plus') +var crypto = require('crypto') +var path = require('path') +var defaultOptions = require('../static/babelrc.json') -let babel = null -let babelVersionDirectory = null +var babel = null +var babelVersionDirectory = null -const defaultOptions = require('../static/babelrc.json') +var PREFIXES = [ + '/** @babel */', + '"use babel"', + '\'use babel\'' +] + +var PREFIX_LENGTH = Math.max.apply(Math, PREFIXES.map(function (prefix) { + return prefix.length +})) exports.shouldCompile = function(sourceCode) { - return sourceCode.startsWith('/** @babel */') || - sourceCode.startsWith('"use babel"') || - sourceCode.startsWith("'use babel'") + var start = sourceCode.substr(0, PREFIX_LENGTH) + return PREFIXES.some(function (prefix) { + return start.indexOf(prefix) === 0 + }) } exports.getCachePath = function(sourceCode) { if (babelVersionDirectory == null) { - let babelVersion = require('babel-core/package.json').version + var babelVersion = require('babel-core/package.json').version babelVersionDirectory = path.join('js', 'babel', createVersionAndOptionsDigest(babelVersion, defaultOptions)) } @@ -35,7 +45,7 @@ exports.compile = function(sourceCode, filePath) { babel = require('babel-core') } - let options = _.defaults({filename: filePath}, defaultOptions) + var options = _.defaults({filename: filePath}, defaultOptions) return babel.transform(sourceCode, options).code } diff --git a/src/coffee-script.js b/src/coffee-script.js index 0aa428ab8..efdcbb72b 100644 --- a/src/coffee-script.js +++ b/src/coffee-script.js @@ -1,13 +1,13 @@ 'use strict' -const crypto = require('crypto') -const path = require('path') +var crypto = require('crypto') +var path = require('path') // The coffee-script compiler is required eagerly because: // 1. It is always used. // 2. It reassigns Error.prepareStackTrace, so we need to make sure that // the 'source-map-support' module is installed *after* it is loaded. -const CoffeeScript = require('coffee-script') +var CoffeeScript = require('coffee-script') exports.shouldCompile = function() { return true @@ -24,13 +24,13 @@ exports.getCachePath = function(sourceCode) { } exports.compile = function(sourceCode, filePath) { - let output = CoffeeScript.compile(sourceCode, { + var output = CoffeeScript.compile(sourceCode, { filename: filePath, sourceFiles: [filePath], sourceMap: true }) - let js = output.js + var js = output.js js += '\n' js += '//# sourceMappingURL=data:application/json;base64,' js += new Buffer(output.v3SourceMap).toString('base64') diff --git a/src/compile-cache.js b/src/compile-cache.js index 821eac45e..0f2554efb 100644 --- a/src/compile-cache.js +++ b/src/compile-cache.js @@ -1,31 +1,31 @@ 'use strict' -const path = require('path') -const CSON = require('season') -const fs = require('fs-plus') +var path = require('path') +var CSON = require('season') +var fs = require('fs-plus') +var _ = require('underscore-plus') -const COMPILERS = { +var COMPILERS = { '.js': require('./babel'), '.ts': require('./typescript'), '.coffee': require('./coffee-script') } -for (let extension in COMPILERS) { - let compiler = COMPILERS[extension] +var cacheDirectory = null + +_.each(COMPILERS, function (compiler, extension) { Object.defineProperty(require.extensions, extension, { enumerable: true, writable: false, value: function (module, filePath) { - let code = compileFileAtPath(compiler, filePath) + var code = compileFileAtPath(compiler, filePath, extension) return module._compile(code, filePath) } }) -} - -let cacheDirectory = null +}) exports.setAtomHomeDirectory = function (atomHome) { - let cacheDir = path.join(atomHome, 'compile-cache') + var cacheDir = path.join(atomHome, 'compile-cache') if (process.env.USER === 'root' && process.env.SUDO_USER && process.env.SUDO_USER !== process.env.USER) { cacheDir = path.join(cacheDirectory, 'root') } @@ -43,21 +43,23 @@ exports.getCacheDirectory = function () { exports.addPathToCache = function (filePath, atomHome) { this.setAtomHomeDirectory(atomHome) - extension = path.extname(filePath) - if (extension === '.cson') { - return CSON.readFileSync(filePath) - } + var extension = path.extname(filePath) - if (compiler = COMPILERS[extension]) { - return compileFileAtPath(compiler, filePath) + if (extension === '.cson') { + CSON.readFileSync(filePath) + } else { + var compiler = COMPILERS[extension] + if (compiler) { + compileFileAtPath(compiler, filePath, extension) + } } } -function compileFileAtPath (compiler, filePath) { - let sourceCode = fs.readFileSync(filePath, 'utf8') +function compileFileAtPath (compiler, filePath, extension) { + var sourceCode = fs.readFileSync(filePath, 'utf8') if (compiler.shouldCompile(sourceCode, filePath)) { - let cachePath = compiler.getCachePath(sourceCode, filePath) - let compiledCode = readCachedJavascript(cachePath) + var cachePath = compiler.getCachePath(sourceCode, filePath) + var compiledCode = readCachedJavascript(cachePath) if (compiledCode == null) { compiledCode = compiler.compile(sourceCode, filePath) writeCachedJavascript(cachePath, compiledCode) @@ -68,7 +70,7 @@ function compileFileAtPath (compiler, filePath) { } function readCachedJavascript (relativeCachePath) { - let cachePath = path.join(cacheDirectory, relativeCachePath) + var cachePath = path.join(cacheDirectory, relativeCachePath) if (fs.isFileSync(cachePath)) { try { return fs.readFileSync(cachePath, 'utf8') @@ -78,11 +80,11 @@ function readCachedJavascript (relativeCachePath) { } function writeCachedJavascript (relativeCachePath, code) { - let cachePath = path.join(cacheDirectory, relativeCachePath) + var cachePath = path.join(cacheDirectory, relativeCachePath) fs.writeFileSync(cachePath, code, 'utf8') } -const INLINE_SOURCE_MAP_REGEXP = /\/\/[#@]\s*sourceMappingURL=([^'"\n]+)\s*$/mg +var INLINE_SOURCE_MAP_REGEXP = /\/\/[#@]\s*sourceMappingURL=([^'"\n]+)\s*$/mg require('source-map-support').install({ handleUncaughtExceptions: false, @@ -95,14 +97,14 @@ require('source-map-support').install({ return null } - let sourceCode = fs.readFileSync(filePath, 'utf8') - let compiler = COMPILERS[path.extname(filePath)] - let fileData = readCachedJavascript(compiler.getCachePath(sourceCode, filePath)) + var sourceCode = fs.readFileSync(filePath, 'utf8') + var compiler = COMPILERS[path.extname(filePath)] + var fileData = readCachedJavascript(compiler.getCachePath(sourceCode, filePath)) if (fileData == null) { return null } - let match, lastMatch + var match, lastMatch INLINE_SOURCE_MAP_REGEXP.lastIndex = 0 while ((match = INLINE_SOURCE_MAP_REGEXP.exec(fileData))) { lastMatch = match @@ -111,9 +113,9 @@ require('source-map-support').install({ return null } - let sourceMappingURL = lastMatch[1] - let rawData = sourceMappingURL.slice(sourceMappingURL.indexOf(',') + 1) - let sourceMap = JSON.parse(new Buffer(rawData, 'base64').toString()) + var sourceMappingURL = lastMatch[1] + var rawData = sourceMappingURL.slice(sourceMappingURL.indexOf(',') + 1) + var sourceMap = JSON.parse(new Buffer(rawData, 'base64').toString()) return { map: sourceMap, diff --git a/src/typescript.js b/src/typescript.js index c372c775b..967fdf18e 100644 --- a/src/typescript.js +++ b/src/typescript.js @@ -1,25 +1,25 @@ 'use strict' -const _ = require('underscore-plus') -const crypto = require('crypto') -const path = require('path') +var _ = require('underscore-plus') +var crypto = require('crypto') +var path = require('path') -let TypeScriptSimple = null -let typescriptVersionDir = null - -const defaultOptions = { +var defaultOptions = { target: 1, module: 'commonjs', sourceMap: true } +var TypeScriptSimple = null +var typescriptVersionDir = null + exports.shouldCompile = function() { return true } exports.getCachePath = function(sourceCode) { if (typescriptVersionDir == null) { - let version = require('typescript-simple/package.json').version + var version = require('typescript-simple/package.json').version typescriptVersionDir = path.join('ts', createVersionAndOptionsDigest(version, defaultOptions)) } @@ -37,7 +37,7 @@ exports.compile = function(sourceCode, filePath) { TypeScriptSimple = require('typescript-simple').TypeScriptSimple } - let options = _.defaults({filename: filePath}, defaultOptions) + var options = _.defaults({filename: filePath}, defaultOptions) return new TypeScriptSimple(options, false).compile(sourceCode, filePath) } From 408202729ec842029a68a730bf2dbff532379f20 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 21 Aug 2015 11:19:11 -0700 Subject: [PATCH 19/30] Add sourceURL comment to all compiled js files --- src/compile-cache.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/compile-cache.js b/src/compile-cache.js index 0f2554efb..c30f03105 100644 --- a/src/compile-cache.js +++ b/src/compile-cache.js @@ -61,7 +61,7 @@ function compileFileAtPath (compiler, filePath, extension) { var cachePath = compiler.getCachePath(sourceCode, filePath) var compiledCode = readCachedJavascript(cachePath) if (compiledCode == null) { - compiledCode = compiler.compile(sourceCode, filePath) + compiledCode = addSourceURL(compiler.compile(sourceCode, filePath), filePath) writeCachedJavascript(cachePath, compiledCode) } return compiledCode @@ -84,6 +84,12 @@ function writeCachedJavascript (relativeCachePath, code) { fs.writeFileSync(cachePath, code, 'utf8') } +function addSourceURL (jsCode, filePath) { + if (process.platform === 'win32') + filePath = '/' + path.resolve(filePath).replace(/\\/g, '/') + return jsCode + '\n' + '//# sourceURL=' + encodeURI(filePath) + '\n' +} + var INLINE_SOURCE_MAP_REGEXP = /\/\/[#@]\s*sourceMappingURL=([^'"\n]+)\s*$/mg require('source-map-support').install({ From bb69f1352dc00348c16a2f5f89e1d9c46a853722 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 21 Aug 2015 11:50:54 -0700 Subject: [PATCH 20/30] :art: --- src/compile-cache.js | 6 +++--- src/typescript.js | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/compile-cache.js b/src/compile-cache.js index c30f03105..ab9f8b663 100644 --- a/src/compile-cache.js +++ b/src/compile-cache.js @@ -99,7 +99,7 @@ require('source-map-support').install({ // source-map-support module, but we've overridden it to read the javascript // code from our cache directory. retrieveSourceMap: function (filePath) { - if (!fs.isFileSync(filePath)){ + if (!fs.isFileSync(filePath)) { return null } @@ -115,13 +115,13 @@ require('source-map-support').install({ while ((match = INLINE_SOURCE_MAP_REGEXP.exec(fileData))) { lastMatch = match } - if (lastMatch == null){ + if (lastMatch == null) { return null } var sourceMappingURL = lastMatch[1] var rawData = sourceMappingURL.slice(sourceMappingURL.indexOf(',') + 1) - var sourceMap = JSON.parse(new Buffer(rawData, 'base64').toString()) + var sourceMap = JSON.parse(new Buffer(rawData, 'base64')) return { map: sourceMap, diff --git a/src/typescript.js b/src/typescript.js index 967fdf18e..60421ef9e 100644 --- a/src/typescript.js +++ b/src/typescript.js @@ -28,7 +28,7 @@ exports.getCachePath = function(sourceCode) { crypto .createHash('sha1') .update(sourceCode, 'utf8') - .digest('hex') + ".js" + .digest('hex') + '.js' ) } From 1f646761898876cd2bf8a707fb2b98a777188d0e Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 21 Aug 2015 12:14:10 -0700 Subject: [PATCH 21/30] :fire: Unnecessary joining and splitting --- spec/atom-reporter.coffee | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/spec/atom-reporter.coffee b/spec/atom-reporter.coffee index 5cd700c39..5e52b4b9b 100644 --- a/spec/atom-reporter.coffee +++ b/spec/atom-reporter.coffee @@ -9,13 +9,10 @@ formatStackTrace = (spec, message='', stackTrace) -> jasminePattern = /^\s*at\s+.*\(?.*[/\\]jasmine(-[^/\\]*)?\.js:\d+:\d+\)?\s*$/ firstJasmineLinePattern = /^\s*at [/\\].*[/\\]jasmine(-[^/\\]*)?\.js:\d+:\d+\)?\s*$/ - convertedLines = [] + lines = [] for line in stackTrace.split('\n') - convertedLines.push(line) unless jasminePattern.test(line) + lines.push(line) unless jasminePattern.test(line) break if firstJasmineLinePattern.test(line) - stackTrace = convertedLines.join('\n') - - lines = stackTrace.split('\n') # Remove first line of stack when it is the same as the error message errorMatch = lines[0]?.match(/^Error: (.*)/) From b533aff8f497224bbf08583a73733dba5002099d Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 21 Aug 2015 13:35:52 -0700 Subject: [PATCH 22/30] :racehorse: Minimize dependencies loaded before the module-cache --- src/babel.js | 6 ++++-- src/compile-cache.js | 9 +++++---- static/index.js | 5 +++++ 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/babel.js b/src/babel.js index e44d78996..8223cb634 100644 --- a/src/babel.js +++ b/src/babel.js @@ -1,6 +1,5 @@ 'use strict' -var _ = require('underscore-plus') var crypto = require('crypto') var path = require('path') var defaultOptions = require('../static/babelrc.json') @@ -45,7 +44,10 @@ exports.compile = function(sourceCode, filePath) { babel = require('babel-core') } - var options = _.defaults({filename: filePath}, defaultOptions) + var options = {filename: filePath} + for (var key in defaultOptions) { + options[key] = defaultOptions[key] + } return babel.transform(sourceCode, options).code } diff --git a/src/compile-cache.js b/src/compile-cache.js index ab9f8b663..366c58800 100644 --- a/src/compile-cache.js +++ b/src/compile-cache.js @@ -1,9 +1,8 @@ 'use strict' var path = require('path') -var CSON = require('season') var fs = require('fs-plus') -var _ = require('underscore-plus') +var CSON = null var COMPILERS = { '.js': require('./babel'), @@ -13,7 +12,9 @@ var COMPILERS = { var cacheDirectory = null -_.each(COMPILERS, function (compiler, extension) { +Object.keys(COMPILERS).forEach(function (extension) { + var compiler = COMPILERS[extension] + Object.defineProperty(require.extensions, extension, { enumerable: true, writable: false, @@ -34,7 +35,6 @@ exports.setAtomHomeDirectory = function (atomHome) { exports.setCacheDirectory = function (directory) { cacheDirectory = directory - CSON.setCacheDir(path.join(cacheDirectory, 'cson')); } exports.getCacheDirectory = function () { @@ -46,6 +46,7 @@ exports.addPathToCache = function (filePath, atomHome) { var extension = path.extname(filePath) if (extension === '.cson') { + CSON = CSON || require('season') CSON.readFileSync(filePath) } else { var compiler = COMPILERS[extension] diff --git a/static/index.js b/static/index.js index 238cb385e..4769837cc 100644 --- a/static/index.js +++ b/static/index.js @@ -78,6 +78,7 @@ var setupWindow = function(loadSettings) { }); setupVmCompatibility(); + setupCsonCache(compileCache.getCacheDirectory()) require(loadSettings.bootstrapScript); require('ipc').sendChannel('window-command', 'window:loaded'); @@ -101,6 +102,10 @@ var setupAtomHome = function() { } } +var setupCsonCache = function(cacheDir) { + require('season').setCacheDir(path.join(cacheDir, 'cson')); +} + var setupVmCompatibility = function() { var vm = require('vm'); if (!vm.Script.createContext) { From f8397661d17148c8183bd5397d8874715c39b2a5 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 21 Aug 2015 13:45:02 -0700 Subject: [PATCH 23/30] Defer coffee-script require, prevent it from reassigning prepareStackTrace --- src/coffee-script.js | 11 +++++------ src/compile-cache.js | 9 +++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/coffee-script.js b/src/coffee-script.js index efdcbb72b..b40a3d9d6 100644 --- a/src/coffee-script.js +++ b/src/coffee-script.js @@ -2,12 +2,7 @@ var crypto = require('crypto') var path = require('path') - -// The coffee-script compiler is required eagerly because: -// 1. It is always used. -// 2. It reassigns Error.prepareStackTrace, so we need to make sure that -// the 'source-map-support' module is installed *after* it is loaded. -var CoffeeScript = require('coffee-script') +var CoffeeScript = null exports.shouldCompile = function() { return true @@ -24,6 +19,10 @@ exports.getCachePath = function(sourceCode) { } exports.compile = function(sourceCode, filePath) { + if (!CoffeeScript) { + CoffeeScript = require('coffee-script') + } + var output = CoffeeScript.compile(sourceCode, { filename: filePath, sourceFiles: [filePath], diff --git a/src/compile-cache.js b/src/compile-cache.js index 366c58800..63ba008ae 100644 --- a/src/compile-cache.js +++ b/src/compile-cache.js @@ -130,3 +130,12 @@ require('source-map-support').install({ } } }) + +// This prevents coffee-script from reassigning `Error.prepareStackTrace` when +// it is loaded. +Object.defineProperty(Error, 'prepareStackTrace', { + value: Error.prepareStackTrace, + configurable: false, + enumerable: true, + writable: false +}) From a6fa098a8cf20afc434e84146b0dfbc6ec37bb99 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 21 Aug 2015 14:47:32 -0700 Subject: [PATCH 24/30] Add CompileCache spec --- spec/compile-cache-spec.coffee | 71 ++++++++++++++++++++++++++++++++++ src/compile-cache.js | 52 ++++++++++++++++++------- 2 files changed, 108 insertions(+), 15 deletions(-) create mode 100644 spec/compile-cache-spec.coffee diff --git a/spec/compile-cache-spec.coffee b/spec/compile-cache-spec.coffee new file mode 100644 index 000000000..94910bb58 --- /dev/null +++ b/spec/compile-cache-spec.coffee @@ -0,0 +1,71 @@ +path = require 'path' +temp = require('temp').track() +Babel = require 'babel-core' +CoffeeScript = require 'coffee-script' +{TypeScriptSimple} = require 'typescript-simple' +CSON = require 'season' +CSONParser = require 'season/node_modules/cson-parser' +CompileCache = require '../src/compile-cache' + +describe 'CompileCache', -> + [atomHome, fixtures] = [] + + beforeEach -> + fixtures = atom.project.getPaths()[0] + atomHome = temp.mkdirSync('fake-atom-home') + + CSON.setCacheDir(null) + CompileCache.resetCacheStats() + + spyOn(Babel, 'transform').andReturn {code: 'the-babel-code'} + spyOn(CoffeeScript, 'compile').andReturn {js: 'the-coffee-code', v3SourceMap: {}} + spyOn(TypeScriptSimple::, 'compile').andReturn 'the-typescript-code' + spyOn(CSONParser, 'parse').andReturn {the: 'cson-data'} + + afterEach -> + CSON.setCacheDir(CompileCache.getCacheDirectory()) + CompileCache.setAtomHomeDirectory(process.env.ATOM_HOME) + + describe 'addPathToCache(filePath, atomHome)', -> + describe 'when the given file is plain javascript', -> + it 'does not compile or cache the file', -> + CompileCache.addPathToCache(path.join(fixtures, 'sample.js'), atomHome) + expect(CompileCache.getCacheStats()['.js']).toEqual {hits: 0, misses: 0} + + describe 'when the given file uses babel', -> + it 'compiles the file with babel and caches it', -> + CompileCache.addPathToCache(path.join(fixtures, 'babel', 'babel-comment.js'), atomHome) + expect(CompileCache.getCacheStats()['.js']).toEqual {hits: 0, misses: 1} + expect(Babel.transform.callCount).toBe 1 + + CompileCache.addPathToCache(path.join(fixtures, 'babel', 'babel-comment.js'), atomHome) + expect(CompileCache.getCacheStats()['.js']).toEqual {hits: 1, misses: 1} + expect(Babel.transform.callCount).toBe 1 + + describe 'when the given file is coffee-script', -> + it 'compiles the file with coffee-script and caches it', -> + CompileCache.addPathToCache(path.join(fixtures, 'coffee.coffee'), atomHome) + expect(CompileCache.getCacheStats()['.coffee']).toEqual {hits: 0, misses: 1} + expect(CoffeeScript.compile.callCount).toBe 1 + + CompileCache.addPathToCache(path.join(fixtures, 'coffee.coffee'), atomHome) + expect(CompileCache.getCacheStats()['.coffee']).toEqual {hits: 1, misses: 1} + expect(CoffeeScript.compile.callCount).toBe 1 + + describe 'when the given file is typescript', -> + it 'compiles the file with typescript and caches it', -> + CompileCache.addPathToCache(path.join(fixtures, 'typescript', 'valid.ts'), atomHome) + expect(CompileCache.getCacheStats()['.ts']).toEqual {hits: 0, misses: 1} + expect(TypeScriptSimple::compile.callCount).toBe 1 + + CompileCache.addPathToCache(path.join(fixtures, 'typescript', 'valid.ts'), atomHome) + expect(CompileCache.getCacheStats()['.ts']).toEqual {hits: 1, misses: 1} + expect(TypeScriptSimple::compile.callCount).toBe 1 + + describe 'when the given file is CSON', -> + it 'compiles the file to JSON and caches it', -> + CompileCache.addPathToCache(path.join(fixtures, 'cson.cson'), atomHome) + expect(CSONParser.parse.callCount).toBe 1 + + CompileCache.addPathToCache(path.join(fixtures, 'cson.cson'), atomHome) + expect(CSONParser.parse.callCount).toBe 1 diff --git a/src/compile-cache.js b/src/compile-cache.js index 63ba008ae..6c8c6cb88 100644 --- a/src/compile-cache.js +++ b/src/compile-cache.js @@ -10,21 +10,9 @@ var COMPILERS = { '.coffee': require('./coffee-script') } +var cacheStats = {} var cacheDirectory = null -Object.keys(COMPILERS).forEach(function (extension) { - var compiler = COMPILERS[extension] - - Object.defineProperty(require.extensions, extension, { - enumerable: true, - writable: false, - value: function (module, filePath) { - var code = compileFileAtPath(compiler, filePath, extension) - return module._compile(code, filePath) - } - }) -}) - exports.setAtomHomeDirectory = function (atomHome) { var cacheDir = path.join(atomHome, 'compile-cache') if (process.env.USER === 'root' && process.env.SUDO_USER && process.env.SUDO_USER !== process.env.USER) { @@ -46,7 +34,10 @@ exports.addPathToCache = function (filePath, atomHome) { var extension = path.extname(filePath) if (extension === '.cson') { - CSON = CSON || require('season') + if (!CSON) { + CSON = require('season') + CSON.setCacheDir(this.getCacheDirectory()) + } CSON.readFileSync(filePath) } else { var compiler = COMPILERS[extension] @@ -56,12 +47,28 @@ exports.addPathToCache = function (filePath, atomHome) { } } +exports.getCacheStats = function () { + return cacheStats +} + +exports.resetCacheStats = function () { + Object.keys(COMPILERS).forEach(function (extension) { + cacheStats[extension] = { + hits: 0, + misses: 0 + } + }) +} + function compileFileAtPath (compiler, filePath, extension) { var sourceCode = fs.readFileSync(filePath, 'utf8') if (compiler.shouldCompile(sourceCode, filePath)) { var cachePath = compiler.getCachePath(sourceCode, filePath) var compiledCode = readCachedJavascript(cachePath) - if (compiledCode == null) { + if (compiledCode != null) { + cacheStats[extension].hits++ + } else { + cacheStats[extension].misses++ compiledCode = addSourceURL(compiler.compile(sourceCode, filePath), filePath) writeCachedJavascript(cachePath, compiledCode) } @@ -139,3 +146,18 @@ Object.defineProperty(Error, 'prepareStackTrace', { enumerable: true, writable: false }) + +Object.keys(COMPILERS).forEach(function (extension) { + var compiler = COMPILERS[extension] + + Object.defineProperty(require.extensions, extension, { + enumerable: true, + writable: false, + value: function (module, filePath) { + var code = compileFileAtPath(compiler, filePath, extension) + return module._compile(code, filePath) + } + }) +}) + +exports.resetCacheStats() From a18b7e2fa9cb7f6c675d6dab1fc0e9303bb17764 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 21 Aug 2015 14:57:39 -0700 Subject: [PATCH 25/30] Don't make prepareStackTrace unwritable This breaks other code (such as Grim) that may want to temporarily use a different prepareStackTrace function. --- src/coffee-script.js | 6 ++++++ src/compile-cache.js | 9 --------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/coffee-script.js b/src/coffee-script.js index b40a3d9d6..de4deb0f1 100644 --- a/src/coffee-script.js +++ b/src/coffee-script.js @@ -20,7 +20,13 @@ exports.getCachePath = function(sourceCode) { exports.compile = function(sourceCode, filePath) { if (!CoffeeScript) { + var previousPrepareStackTrace = Error.prepareStackTrace CoffeeScript = require('coffee-script') + + // When it loads, coffee-script reassigns Error.prepareStackTrace. We have + // already reassigned it via the 'source-map-support' module, so we need + // to set it back. + Error.prepareStackTrace = previousPrepareStackTrace } var output = CoffeeScript.compile(sourceCode, { diff --git a/src/compile-cache.js b/src/compile-cache.js index 6c8c6cb88..6a261b70d 100644 --- a/src/compile-cache.js +++ b/src/compile-cache.js @@ -138,15 +138,6 @@ require('source-map-support').install({ } }) -// This prevents coffee-script from reassigning `Error.prepareStackTrace` when -// it is loaded. -Object.defineProperty(Error, 'prepareStackTrace', { - value: Error.prepareStackTrace, - configurable: false, - enumerable: true, - writable: false -}) - Object.keys(COMPILERS).forEach(function (extension) { var compiler = COMPILERS[extension] From f3778148a065c713b98738d42cd7acb49c0b8ae0 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 21 Aug 2015 15:37:36 -0700 Subject: [PATCH 26/30] Catch errors when retrieving source maps --- src/compile-cache.js | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/src/compile-cache.js b/src/compile-cache.js index 6a261b70d..ae51d4980 100644 --- a/src/compile-cache.js +++ b/src/compile-cache.js @@ -111,9 +111,22 @@ require('source-map-support').install({ return null } - var sourceCode = fs.readFileSync(filePath, 'utf8') + try { + var sourceCode = fs.readFileSync(filePath, 'utf8') + } catch (error) { + console.warn("Error reading source file", error.stack) + return null + } + var compiler = COMPILERS[path.extname(filePath)] - var fileData = readCachedJavascript(compiler.getCachePath(sourceCode, filePath)) + + try { + var fileData = readCachedJavascript(compiler.getCachePath(sourceCode, filePath)) + } catch (error) { + console.warn("Error reading compiled file", error.stack) + return null + } + if (fileData == null) { return null } @@ -129,7 +142,13 @@ require('source-map-support').install({ var sourceMappingURL = lastMatch[1] var rawData = sourceMappingURL.slice(sourceMappingURL.indexOf(',') + 1) - var sourceMap = JSON.parse(new Buffer(rawData, 'base64')) + + try { + var sourceMap = JSON.parse(new Buffer(rawData, 'base64')) + } catch (error) { + console.warn("Error parsing source map", error.stack) + return null + } return { map: sourceMap, From ca4922bb70c20730e4446aed7e3af00a133d0a61 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 21 Aug 2015 16:00:39 -0700 Subject: [PATCH 27/30] :fire: File extension --- build/Gruntfile.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index 860616745..e4fbb39c6 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -3,7 +3,7 @@ path = require 'path' os = require 'os' glob = require 'glob' usesBabel = require './lib/uses-babel' -babelOptions = require('../static/babelrc.json') +babelOptions = require '../static/babelrc' # Add support for obselete APIs of vm module so we can make some third-party # modules work under node v0.11.x. From a4b9b9c6cd27e1403bd1ea4b82bd02f97031fc6a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 21 Aug 2015 16:56:32 -0700 Subject: [PATCH 28/30] :shirt: Use standard style for js code --- build/Gruntfile.coffee | 9 +- build/package.json | 1 + src/babel.js | 8 +- src/coffee-script.js | 10 +- src/compile-cache.js | 9 +- src/typescript.js | 6 +- static/index.js | 368 ++++++++++++++++++++--------------------- 7 files changed, 209 insertions(+), 202 deletions(-) diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index e4fbb39c6..d89a86531 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -21,6 +21,7 @@ module.exports = (grunt) -> grunt.loadNpmTasks('grunt-babel') grunt.loadNpmTasks('grunt-coffeelint') grunt.loadNpmTasks('grunt-lesslint') + grunt.loadNpmTasks('grunt-standard') grunt.loadNpmTasks('grunt-cson') grunt.loadNpmTasks('grunt-contrib-csslint') grunt.loadNpmTasks('grunt-contrib-coffee') @@ -192,6 +193,12 @@ module.exports = (grunt) -> 'spec/*.coffee' ] + standard: + src: [ + 'src/**/*.js' + 'static/*.js' + ] + csslint: options: 'adjoining-classes': false @@ -248,7 +255,7 @@ module.exports = (grunt) -> failOnError: false grunt.registerTask('compile', ['babel', 'coffee', 'prebuild-less', 'cson', 'peg']) - grunt.registerTask('lint', ['coffeelint', 'csslint', 'lesslint']) + grunt.registerTask('lint', ['standard', 'coffeelint', 'csslint', 'lesslint']) grunt.registerTask('test', ['shell:kill-atom', 'run-specs']) ciTasks = ['output-disk-space', 'download-atom-shell', 'download-atom-shell-chromedriver', 'build'] diff --git a/build/package.json b/build/package.json index 07e9fa7ad..251cc1ba6 100644 --- a/build/package.json +++ b/build/package.json @@ -26,6 +26,7 @@ "grunt-lesslint": "0.17.0", "grunt-peg": "~1.1.0", "grunt-shell": "~0.3.1", + "grunt-standard": "^1.0.2", "harmony-collections": "~0.3.8", "legal-eagle": "~0.10.0", "minidump": "~0.9", diff --git a/src/babel.js b/src/babel.js index 8223cb634..f53dbc758 100644 --- a/src/babel.js +++ b/src/babel.js @@ -17,14 +17,14 @@ var PREFIX_LENGTH = Math.max.apply(Math, PREFIXES.map(function (prefix) { return prefix.length })) -exports.shouldCompile = function(sourceCode) { +exports.shouldCompile = function (sourceCode) { var start = sourceCode.substr(0, PREFIX_LENGTH) return PREFIXES.some(function (prefix) { return start.indexOf(prefix) === 0 }) } -exports.getCachePath = function(sourceCode) { +exports.getCachePath = function (sourceCode) { if (babelVersionDirectory == null) { var babelVersion = require('babel-core/package.json').version babelVersionDirectory = path.join('js', 'babel', createVersionAndOptionsDigest(babelVersion, defaultOptions)) @@ -35,11 +35,11 @@ exports.getCachePath = function(sourceCode) { crypto .createHash('sha1') .update(sourceCode, 'utf8') - .digest('hex') + ".js" + .digest('hex') + '.js' ) } -exports.compile = function(sourceCode, filePath) { +exports.compile = function (sourceCode, filePath) { if (!babel) { babel = require('babel-core') } diff --git a/src/coffee-script.js b/src/coffee-script.js index de4deb0f1..90f23bfa5 100644 --- a/src/coffee-script.js +++ b/src/coffee-script.js @@ -4,21 +4,21 @@ var crypto = require('crypto') var path = require('path') var CoffeeScript = null -exports.shouldCompile = function() { +exports.shouldCompile = function () { return true } -exports.getCachePath = function(sourceCode) { +exports.getCachePath = function (sourceCode) { return path.join( - "coffee", + 'coffee', crypto .createHash('sha1') .update(sourceCode, 'utf8') - .digest('hex') + ".js" + .digest('hex') + '.js' ) } -exports.compile = function(sourceCode, filePath) { +exports.compile = function (sourceCode, filePath) { if (!CoffeeScript) { var previousPrepareStackTrace = Error.prepareStackTrace CoffeeScript = require('coffee-script') diff --git a/src/compile-cache.js b/src/compile-cache.js index ae51d4980..9e4d14e15 100644 --- a/src/compile-cache.js +++ b/src/compile-cache.js @@ -93,8 +93,9 @@ function writeCachedJavascript (relativeCachePath, code) { } function addSourceURL (jsCode, filePath) { - if (process.platform === 'win32') + if (process.platform === 'win32') { filePath = '/' + path.resolve(filePath).replace(/\\/g, '/') + } return jsCode + '\n' + '//# sourceURL=' + encodeURI(filePath) + '\n' } @@ -114,7 +115,7 @@ require('source-map-support').install({ try { var sourceCode = fs.readFileSync(filePath, 'utf8') } catch (error) { - console.warn("Error reading source file", error.stack) + console.warn('Error reading source file', error.stack) return null } @@ -123,7 +124,7 @@ require('source-map-support').install({ try { var fileData = readCachedJavascript(compiler.getCachePath(sourceCode, filePath)) } catch (error) { - console.warn("Error reading compiled file", error.stack) + console.warn('Error reading compiled file', error.stack) return null } @@ -146,7 +147,7 @@ require('source-map-support').install({ try { var sourceMap = JSON.parse(new Buffer(rawData, 'base64')) } catch (error) { - console.warn("Error parsing source map", error.stack) + console.warn('Error parsing source map', error.stack) return null } diff --git a/src/typescript.js b/src/typescript.js index 60421ef9e..c942f542a 100644 --- a/src/typescript.js +++ b/src/typescript.js @@ -13,11 +13,11 @@ var defaultOptions = { var TypeScriptSimple = null var typescriptVersionDir = null -exports.shouldCompile = function() { +exports.shouldCompile = function () { return true } -exports.getCachePath = function(sourceCode) { +exports.getCachePath = function (sourceCode) { if (typescriptVersionDir == null) { var version = require('typescript-simple/package.json').version typescriptVersionDir = path.join('ts', createVersionAndOptionsDigest(version, defaultOptions)) @@ -32,7 +32,7 @@ exports.getCachePath = function(sourceCode) { ) } -exports.compile = function(sourceCode, filePath) { +exports.compile = function (sourceCode, filePath) { if (!TypeScriptSimple) { TypeScriptSimple = require('typescript-simple').TypeScriptSimple } diff --git a/static/index.js b/static/index.js index 4769837cc..914290321 100644 --- a/static/index.js +++ b/static/index.js @@ -1,199 +1,197 @@ -(function() { +(function () { + var fs = require('fs') + var path = require('path') -var fs = require('fs'); -var path = require('path'); + var loadSettings = null + var loadSettingsError = null -var loadSettings = null; -var loadSettingsError = null; - -window.onload = function() { - try { - var startTime = Date.now(); - - process.on('unhandledRejection', function(error, promise) { - console.error('Unhandled promise rejection %o with error: %o', promise, error); - }); - - // Ensure ATOM_HOME is always set before anything else is required - setupAtomHome(); - - // Normalize to make sure drive letter case is consistent on Windows - process.resourcesPath = path.normalize(process.resourcesPath); - - if (loadSettingsError) { - throw loadSettingsError; - } - - var devMode = loadSettings.devMode || !loadSettings.resourcePath.startsWith(process.resourcesPath + path.sep); - - if (devMode) { - setupDeprecatedPackages(); - } - - if (loadSettings.profileStartup) { - profileStartup(loadSettings, Date.now() - startTime); - } else { - setupWindow(loadSettings); - setLoadTime(Date.now() - startTime); - } - } catch (error) { - handleSetupError(error); - } -} - -var setLoadTime = function(loadTime) { - if (global.atom) { - global.atom.loadTime = loadTime; - console.log('Window load time: ' + global.atom.getWindowLoadTime() + 'ms'); - } -} - -var handleSetupError = function(error) { - var currentWindow = require('remote').getCurrentWindow(); - currentWindow.setSize(800, 600); - currentWindow.center(); - currentWindow.show(); - currentWindow.openDevTools(); - console.error(error.stack || error); -} - -var setupWindow = function(loadSettings) { - var compileCache = require('../src/compile-cache') - compileCache.setAtomHomeDirectory(process.env.ATOM_HOME) - - ModuleCache = require('../src/module-cache'); - ModuleCache.register(loadSettings); - ModuleCache.add(loadSettings.resourcePath); - - // Only include deprecated APIs when running core spec - require('grim').includeDeprecatedAPIs = isRunningCoreSpecs(loadSettings); - - // Start the crash reporter before anything else. - require('crash-reporter').start({ - productName: 'Atom', - companyName: 'GitHub', - // By explicitly passing the app version here, we could save the call - // of "require('remote').require('app').getVersion()". - extra: {_version: loadSettings.appVersion} - }); - - setupVmCompatibility(); - setupCsonCache(compileCache.getCacheDirectory()) - - require(loadSettings.bootstrapScript); - require('ipc').sendChannel('window-command', 'window:loaded'); -} - -var setupAtomHome = function() { - if (!process.env.ATOM_HOME) { - var home; - if (process.platform === 'win32') { - home = process.env.USERPROFILE; - } else { - home = process.env.HOME; - } - var atomHome = path.join(home, '.atom'); - try { - atomHome = fs.realpathSync(atomHome); - } catch (error) { - // Ignore since the path might just not exist yet. - } - process.env.ATOM_HOME = atomHome; - } -} - -var setupCsonCache = function(cacheDir) { - require('season').setCacheDir(path.join(cacheDir, 'cson')); -} - -var setupVmCompatibility = function() { - var vm = require('vm'); - if (!vm.Script.createContext) { - vm.Script.createContext = vm.createContext; - } -} - -var setupDeprecatedPackages = function() { - var metadata = require('../package.json'); - if (!metadata._deprecatedPackages) { - try { - metadata._deprecatedPackages = require('../build/deprecated-packages.json'); - } catch(requireError) { - console.error('Failed to setup deprecated packages list', requireError.stack); - } - } -} - -var profileStartup = function(loadSettings, initialTime) { - var profile = function() { - console.profile('startup'); + window.onload = function () { try { var startTime = Date.now() - setupWindow(loadSettings); - setLoadTime(Date.now() - startTime + initialTime); + + process.on('unhandledRejection', function (error, promise) { + console.error('Unhandled promise rejection %o with error: %o', promise, error) + }) + + // Ensure ATOM_HOME is always set before anything else is required + setupAtomHome() + + // Normalize to make sure drive letter case is consistent on Windows + process.resourcesPath = path.normalize(process.resourcesPath) + + if (loadSettingsError) { + throw loadSettingsError + } + + var devMode = loadSettings.devMode || !loadSettings.resourcePath.startsWith(process.resourcesPath + path.sep) + + if (devMode) { + setupDeprecatedPackages() + } + + if (loadSettings.profileStartup) { + profileStartup(loadSettings, Date.now() - startTime) + } else { + setupWindow(loadSettings) + setLoadTime(Date.now() - startTime) + } } catch (error) { - handleSetupError(error); - } finally { - console.profileEnd('startup'); - console.log("Switch to the Profiles tab to view the created startup profile") + handleSetupError(error) } - }; - - var currentWindow = require('remote').getCurrentWindow(); - if (currentWindow.devToolsWebContents) { - profile(); - } else { - currentWindow.openDevTools(); - currentWindow.once('devtools-opened', function() { - setTimeout(profile, 100); - }); - } -} - -var parseLoadSettings = function() { - var rawLoadSettings = decodeURIComponent(location.hash.substr(1)); - try { - loadSettings = JSON.parse(rawLoadSettings); - } catch (error) { - console.error("Failed to parse load settings: " + rawLoadSettings); - loadSettingsError = error; - } -} - -var setupWindowBackground = function() { - if (loadSettings && loadSettings.isSpec) { - return; } - var backgroundColor = window.localStorage.getItem('atom:window-background-color'); - if (!backgroundColor) { - return; + function setLoadTime (loadTime) { + if (global.atom) { + global.atom.loadTime = loadTime + console.log('Window load time: ' + global.atom.getWindowLoadTime() + 'ms') + } } - var backgroundStylesheet = document.createElement('style'); - backgroundStylesheet.type = 'text/css'; - backgroundStylesheet.innerText = 'html, body { background: ' + backgroundColor + '; }'; - document.head.appendChild(backgroundStylesheet); + function handleSetupError (error) { + var currentWindow = require('remote').getCurrentWindow() + currentWindow.setSize(800, 600) + currentWindow.center() + currentWindow.show() + currentWindow.openDevTools() + console.error(error.stack || error) + } - // Remove once the page loads - window.addEventListener("load", function loadWindow() { - window.removeEventListener("load", loadWindow, false); - setTimeout(function() { - backgroundStylesheet.remove(); - backgroundStylesheet = null; - }, 1000); - }, false); -} + function setupWindow (loadSettings) { + var CompileCache = require('../src/compile-cache') + CompileCache.setAtomHomeDirectory(process.env.ATOM_HOME) -var isRunningCoreSpecs = function(loadSettings) { - return !!(loadSettings && - loadSettings.isSpec && - loadSettings.specDirectory && - loadSettings.resourcePath && - path.dirname(loadSettings.specDirectory) === loadSettings.resourcePath); -} + var ModuleCache = require('../src/module-cache') + ModuleCache.register(loadSettings) + ModuleCache.add(loadSettings.resourcePath) -parseLoadSettings(); -setupWindowBackground(); + // Only include deprecated APIs when running core spec + require('grim').includeDeprecatedAPIs = isRunningCoreSpecs(loadSettings) -})(); + // Start the crash reporter before anything else. + require('crash-reporter').start({ + productName: 'Atom', + companyName: 'GitHub', + // By explicitly passing the app version here, we could save the call + // of "require('remote').require('app').getVersion()". + extra: {_version: loadSettings.appVersion} + }) + + setupVmCompatibility() + setupCsonCache(CompileCache.getCacheDirectory()) + + require(loadSettings.bootstrapScript) + require('ipc').sendChannel('window-command', 'window:loaded') + } + + function setupAtomHome () { + if (!process.env.ATOM_HOME) { + var home + if (process.platform === 'win32') { + home = process.env.USERPROFILE + } else { + home = process.env.HOME + } + var atomHome = path.join(home, '.atom') + try { + atomHome = fs.realpathSync(atomHome) + } catch (error) { + // Ignore since the path might just not exist yet. + } + process.env.ATOM_HOME = atomHome + } + } + + function setupCsonCache (cacheDir) { + require('season').setCacheDir(path.join(cacheDir, 'cson')) + } + + function setupVmCompatibility () { + var vm = require('vm') + if (!vm.Script.createContext) { + vm.Script.createContext = vm.createContext + } + } + + function setupDeprecatedPackages () { + var metadata = require('../package.json') + if (!metadata._deprecatedPackages) { + try { + metadata._deprecatedPackages = require('../build/deprecated-packages.json') + } catch(requireError) { + console.error('Failed to setup deprecated packages list', requireError.stack) + } + } + } + + function profileStartup (loadSettings, initialTime) { + function profile () { + console.profile('startup') + try { + var startTime = Date.now() + setupWindow(loadSettings) + setLoadTime(Date.now() - startTime + initialTime) + } catch (error) { + handleSetupError(error) + } finally { + console.profileEnd('startup') + console.log('Switch to the Profiles tab to view the created startup profile') + } + } + + var currentWindow = require('remote').getCurrentWindow() + if (currentWindow.devToolsWebContents) { + profile() + } else { + currentWindow.openDevTools() + currentWindow.once('devtools-opened', function () { + setTimeout(profile, 100) + }) + } + } + + function parseLoadSettings () { + var rawLoadSettings = decodeURIComponent(window.location.hash.substr(1)) + try { + loadSettings = JSON.parse(rawLoadSettings) + } catch (error) { + console.error('Failed to parse load settings: ' + rawLoadSettings) + loadSettingsError = error + } + } + + function setupWindowBackground () { + if (loadSettings && loadSettings.isSpec) { + return + } + + var backgroundColor = window.localStorage.getItem('atom:window-background-color') + if (!backgroundColor) { + return + } + + var backgroundStylesheet = document.createElement('style') + backgroundStylesheet.type = 'text/css' + backgroundStylesheet.innerText = 'html, body { background: ' + backgroundColor + '; }' + document.head.appendChild(backgroundStylesheet) + + // Remove once the page loads + window.addEventListener('load', function loadWindow () { + window.removeEventListener('load', loadWindow, false) + setTimeout(function () { + backgroundStylesheet.remove() + backgroundStylesheet = null + }, 1000) + }, false) + } + + function isRunningCoreSpecs (loadSettings) { + return !!(loadSettings && + loadSettings.isSpec && + loadSettings.specDirectory && + loadSettings.resourcePath && + path.dirname(loadSettings.specDirectory) === loadSettings.resourcePath) + } + + parseLoadSettings() + setupWindowBackground() +})() From 0dd19f3133479ad5405cffd19d0574c0f3dcfa1a Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 21 Aug 2015 16:59:53 -0700 Subject: [PATCH 29/30] Remove 6to5 fixtures --- spec/fixtures/babel/6to5-double-quotes.js | 3 --- spec/fixtures/babel/6to5-single-quotes.js | 3 --- 2 files changed, 6 deletions(-) delete mode 100644 spec/fixtures/babel/6to5-double-quotes.js delete mode 100644 spec/fixtures/babel/6to5-single-quotes.js diff --git a/spec/fixtures/babel/6to5-double-quotes.js b/spec/fixtures/babel/6to5-double-quotes.js deleted file mode 100644 index 254912bc0..000000000 --- a/spec/fixtures/babel/6to5-double-quotes.js +++ /dev/null @@ -1,3 +0,0 @@ -"use 6to5"; - -module.exports = v => v + 1 diff --git a/spec/fixtures/babel/6to5-single-quotes.js b/spec/fixtures/babel/6to5-single-quotes.js deleted file mode 100644 index 16e382ec2..000000000 --- a/spec/fixtures/babel/6to5-single-quotes.js +++ /dev/null @@ -1,3 +0,0 @@ -'use 6to5'; - -module.exports = v => v + 1 From 3a66096ea43aa50115917be3c4af9927862113d0 Mon Sep 17 00:00:00 2001 From: Max Brunsfeld Date: Fri, 21 Aug 2015 17:25:25 -0700 Subject: [PATCH 30/30] Remove harmony-collections shim in Gruntfile eslint cannot load if this shim is present. --- build/Gruntfile.coffee | 4 ---- build/package.json | 1 - 2 files changed, 5 deletions(-) diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index d89a86531..d21e8fa4a 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -13,10 +13,6 @@ _ = require 'underscore-plus' packageJson = require '../package.json' -# Shim harmony collections in case grunt was invoked without harmony -# collections enabled -_.extend(global, require('harmony-collections')) unless global.WeakMap? - module.exports = (grunt) -> grunt.loadNpmTasks('grunt-babel') grunt.loadNpmTasks('grunt-coffeelint') diff --git a/build/package.json b/build/package.json index 251cc1ba6..bd0c9a474 100644 --- a/build/package.json +++ b/build/package.json @@ -27,7 +27,6 @@ "grunt-peg": "~1.1.0", "grunt-shell": "~0.3.1", "grunt-standard": "^1.0.2", - "harmony-collections": "~0.3.8", "legal-eagle": "~0.10.0", "minidump": "~0.9", "npm": "2.13.3",