diff --git a/README.md b/README.md index 694c1b9..c88a3c0 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ And run `webpack` via your preferred method. ## Options Type: `Object` -Default: `{}` +Default: `{ bare: true }` Options for CoffeeScript. All possible options you can find [here](https://coffeescript.org/#nodejs-usage). diff --git a/src/index.js b/src/index.js index 24eaa0d..022856f 100644 --- a/src/index.js +++ b/src/index.js @@ -26,7 +26,7 @@ export default function loader(source) { try { result = coffeescript.compile(source, { - ...{ sourceMap: useSourceMap }, + ...{ sourceMap: useSourceMap, bare: true }, ...options, ...{ filename: this.resourcePath }, }); diff --git a/test/__snapshots__/loader.test.js.snap b/test/__snapshots__/loader.test.js.snap index 16230e9..5c80025 100644 --- a/test/__snapshots__/loader.test.js.snap +++ b/test/__snapshots__/loader.test.js.snap @@ -18,6 +18,27 @@ CoffeeScriptError: Error: Cannot find module '@babel/preset-env1' from ''", exports[`loader should throw an error on invalid CoffeeScript options: warnings 1`] = `Array []`; +exports[`loader should work and code without the top-level function safety wrapper: errors 1`] = `Array []`; + +exports[`loader should work and code without the top-level function safety wrapper: source 1`] = ` +"var changeNumbers, inner, outer; + +outer = 1; + +changeNumbers = function() { + var inner; + inner = -1; + return outer = 10; +}; + +inner = changeNumbers(); +" +`; + +exports[`loader should work and code without the top-level function safety wrapper: sourceMap 1`] = `undefined`; + +exports[`loader should work and code without the top-level function safety wrapper: warnings 1`] = `Array []`; + exports[`loader should work and generate source maps (take value from the \`compiler.devtool\` option): errors 1`] = `Array []`; exports[`loader should work and generate source maps (take value from the \`compiler.devtool\` option): source 1`] = ` @@ -614,192 +635,189 @@ exports[`loader should work and support CoffeeScript options: warnings 1`] = `Ar exports[`loader should work for Literate CoffeeScript: errors 1`] = `Array []`; exports[`loader should work for Literate CoffeeScript: source 1`] = ` -"(function() { - // The **Scope** class regulates lexical scoping within CoffeeScript. As you +" // The **Scope** class regulates lexical scoping within CoffeeScript. As you // generate code, you create a tree of scopes in the same shape as the nested // function bodies. Each scope knows about the variables declared within it, // and has a reference to its parent enclosing scope. In this way, we know which // variables are new and need to be declared with \`var\`, and which are shared // with external scopes. - var Scope, - indexOf = [].indexOf; - - exports.Scope = Scope = class Scope { - // Initialize a scope with its parent, for lookups up the chain, - // as well as a reference to the **Block** node it belongs to, which is - // where it should declare its variables, a reference to the function that - // it belongs to, and a list of variables referenced in the source code - // and therefore should be avoided when generating variables. Also track comments - // that should be output as part of variable declarations. - constructor(parent, expressions, method, referencedVars) { - var ref, ref1; - this.parent = parent; - this.expressions = expressions; - this.method = method; - this.referencedVars = referencedVars; - this.variables = [ - { - name: 'arguments', - type: 'arguments' - } - ]; - this.comments = {}; - this.positions = {}; - if (!this.parent) { - this.utilities = {}; +var Scope, + indexOf = [].indexOf; + +exports.Scope = Scope = class Scope { + // Initialize a scope with its parent, for lookups up the chain, + // as well as a reference to the **Block** node it belongs to, which is + // where it should declare its variables, a reference to the function that + // it belongs to, and a list of variables referenced in the source code + // and therefore should be avoided when generating variables. Also track comments + // that should be output as part of variable declarations. + constructor(parent, expressions, method, referencedVars) { + var ref, ref1; + this.parent = parent; + this.expressions = expressions; + this.method = method; + this.referencedVars = referencedVars; + this.variables = [ + { + name: 'arguments', + type: 'arguments' } - // The \`@root\` is the top-level **Scope** object for a given file. - this.root = (ref = (ref1 = this.parent) != null ? ref1.root : void 0) != null ? ref : this; + ]; + this.comments = {}; + this.positions = {}; + if (!this.parent) { + this.utilities = {}; } + // The \`@root\` is the top-level **Scope** object for a given file. + this.root = (ref = (ref1 = this.parent) != null ? ref1.root : void 0) != null ? ref : this; + } - // Adds a new variable or overrides an existing one. - add(name, type, immediate) { - if (this.shared && !immediate) { - return this.parent.add(name, type, immediate); - } - if (Object.prototype.hasOwnProperty.call(this.positions, name)) { - return this.variables[this.positions[name]].type = type; - } else { - return this.positions[name] = this.variables.push({name, type}) - 1; - } + // Adds a new variable or overrides an existing one. + add(name, type, immediate) { + if (this.shared && !immediate) { + return this.parent.add(name, type, immediate); } - - // When \`super\` is called, we need to find the name of the current method we're - // in, so that we know how to invoke the same method of the parent class. This - // can get complicated if super is being called from an inner function. - // \`namedMethod\` will walk up the scope tree until it either finds the first - // function object that has a name filled in, or bottoms out. - namedMethod() { - var ref; - if (((ref = this.method) != null ? ref.name : void 0) || !this.parent) { - return this.method; - } - return this.parent.namedMethod(); + if (Object.prototype.hasOwnProperty.call(this.positions, name)) { + return this.variables[this.positions[name]].type = type; + } else { + return this.positions[name] = this.variables.push({name, type}) - 1; } + } - // Look up a variable name in lexical scope, and declare it if it does not - // already exist. - find(name, type = 'var') { - if (this.check(name)) { - return true; - } - this.add(name, type); - return false; + // When \`super\` is called, we need to find the name of the current method we're + // in, so that we know how to invoke the same method of the parent class. This + // can get complicated if super is being called from an inner function. + // \`namedMethod\` will walk up the scope tree until it either finds the first + // function object that has a name filled in, or bottoms out. + namedMethod() { + var ref; + if (((ref = this.method) != null ? ref.name : void 0) || !this.parent) { + return this.method; } + return this.parent.namedMethod(); + } - // Reserve a variable name as originating from a function parameter for this - // scope. No \`var\` required for internal references. - parameter(name) { - if (this.shared && this.parent.check(name, true)) { - return; - } - return this.add(name, 'param'); + // Look up a variable name in lexical scope, and declare it if it does not + // already exist. + find(name, type = 'var') { + if (this.check(name)) { + return true; } + this.add(name, type); + return false; + } - // Just check to see if a variable has already been declared, without reserving, - // walks up to the root scope. - check(name) { - var ref; - return !!(this.type(name) || ((ref = this.parent) != null ? ref.check(name) : void 0)); + // Reserve a variable name as originating from a function parameter for this + // scope. No \`var\` required for internal references. + parameter(name) { + if (this.shared && this.parent.check(name, true)) { + return; } + return this.add(name, 'param'); + } - // Generate a temporary variable name at the given index. - temporary(name, index, single = false) { - var diff, endCode, letter, newCode, num, startCode; - if (single) { - startCode = name.charCodeAt(0); - endCode = 'z'.charCodeAt(0); - diff = endCode - startCode; - newCode = startCode + index % (diff + 1); - letter = String.fromCharCode(newCode); - num = Math.floor(index / (diff + 1)); - return \`\${letter}\${num || ''}\`; - } else { - return \`\${name}\${index || ''}\`; - } + // Just check to see if a variable has already been declared, without reserving, + // walks up to the root scope. + check(name) { + var ref; + return !!(this.type(name) || ((ref = this.parent) != null ? ref.check(name) : void 0)); + } + + // Generate a temporary variable name at the given index. + temporary(name, index, single = false) { + var diff, endCode, letter, newCode, num, startCode; + if (single) { + startCode = name.charCodeAt(0); + endCode = 'z'.charCodeAt(0); + diff = endCode - startCode; + newCode = startCode + index % (diff + 1); + letter = String.fromCharCode(newCode); + num = Math.floor(index / (diff + 1)); + return \`\${letter}\${num || ''}\`; + } else { + return \`\${name}\${index || ''}\`; } + } - // Gets the type of a variable. - type(name) { - var i, len, ref, v; - ref = this.variables; - for (i = 0, len = ref.length; i < len; i++) { - v = ref[i]; - if (v.name === name) { - return v.type; - } + // Gets the type of a variable. + type(name) { + var i, len, ref, v; + ref = this.variables; + for (i = 0, len = ref.length; i < len; i++) { + v = ref[i]; + if (v.name === name) { + return v.type; } - return null; } + return null; + } - // If we need to store an intermediate result, find an available name for a - // compiler-generated variable. \`_var\`, \`_var2\`, and so on... - freeVariable(name, options = {}) { - var index, ref, temp; - index = 0; - while (true) { - temp = this.temporary(name, index, options.single); - if (!(this.check(temp) || indexOf.call(this.root.referencedVars, temp) >= 0)) { - break; - } - index++; + // If we need to store an intermediate result, find an available name for a + // compiler-generated variable. \`_var\`, \`_var2\`, and so on... + freeVariable(name, options = {}) { + var index, ref, temp; + index = 0; + while (true) { + temp = this.temporary(name, index, options.single); + if (!(this.check(temp) || indexOf.call(this.root.referencedVars, temp) >= 0)) { + break; } - if ((ref = options.reserve) != null ? ref : true) { - this.add(temp, 'var', true); - } - return temp; + index++; } - - // Ensure that an assignment is made at the top of this scope - // (or at the top-level scope, if requested). - assign(name, value) { - this.add(name, { - value, - assigned: true - }, true); - return this.hasAssignments = true; + if ((ref = options.reserve) != null ? ref : true) { + this.add(temp, 'var', true); } + return temp; + } - // Does this scope have any declared variables? - hasDeclarations() { - return !!this.declaredVariables().length; - } + // Ensure that an assignment is made at the top of this scope + // (or at the top-level scope, if requested). + assign(name, value) { + this.add(name, { + value, + assigned: true + }, true); + return this.hasAssignments = true; + } - // Return the list of variables first declared in this scope. - declaredVariables() { - var v; - return ((function() { - var i, len, ref, results; - ref = this.variables; - results = []; - for (i = 0, len = ref.length; i < len; i++) { - v = ref[i]; - if (v.type === 'var') { - results.push(v.name); - } - } - return results; - }).call(this)).sort(); - } + // Does this scope have any declared variables? + hasDeclarations() { + return !!this.declaredVariables().length; + } - // Return the list of assignments that are supposed to be made at the top - // of this scope. - assignedVariables() { - var i, len, ref, results, v; + // Return the list of variables first declared in this scope. + declaredVariables() { + var v; + return ((function() { + var i, len, ref, results; ref = this.variables; results = []; for (i = 0, len = ref.length; i < len; i++) { v = ref[i]; - if (v.type.assigned) { - results.push(\`\${v.name} = \${v.type.value}\`); + if (v.type === 'var') { + results.push(v.name); } } return results; - } + }).call(this)).sort(); + } - }; + // Return the list of assignments that are supposed to be made at the top + // of this scope. + assignedVariables() { + var i, len, ref, results, v; + ref = this.variables; + results = []; + for (i = 0, len = ref.length; i < len; i++) { + v = ref[i]; + if (v.type.assigned) { + results.push(\`\${v.name} = \${v.type.value}\`); + } + } + return results; + } -}).call(this); +}; " `; diff --git a/test/fixtures/bare.coffee b/test/fixtures/bare.coffee new file mode 100644 index 0000000..d0d5129 --- /dev/null +++ b/test/fixtures/bare.coffee @@ -0,0 +1,5 @@ +outer = 1 +changeNumbers = -> + inner = -1 + outer = 10 +inner = changeNumbers() diff --git a/test/fixtures/bare.js b/test/fixtures/bare.js new file mode 100644 index 0000000..242548b --- /dev/null +++ b/test/fixtures/bare.js @@ -0,0 +1,3 @@ +import foo from './bare.coffee'; + +export default foo; diff --git a/test/loader.test.js b/test/loader.test.js index 18377af..91f2ee3 100644 --- a/test/loader.test.js +++ b/test/loader.test.js @@ -21,6 +21,19 @@ describe('loader', () => { expect(getWarnings(stats)).toMatchSnapshot('warnings'); }); + it('should work and code without the top-level function safety wrapper', async () => { + const compiler = getCompiler('bare.js'); + const stats = await compile(compiler); + const { source, sourceMap } = execute( + getModuleSource('./bare.coffee', stats) + ); + + expect(source).toMatchSnapshot('source'); + expect(sourceMap).toMatchSnapshot('sourceMap'); + expect(getErrors(stats)).toMatchSnapshot('errors'); + expect(getWarnings(stats)).toMatchSnapshot('warnings'); + }); + it('should generate an error on broken code', async () => { const compiler = getCompiler('bar.js'); const stats = await compile(compiler);