diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..77c0e5e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +# http://editorconfig.org/ + +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 2 +trim_trailing_whitespace = true diff --git a/.eslintrc.yml b/.eslintrc.yml new file mode 100644 index 0000000..12cad61 --- /dev/null +++ b/.eslintrc.yml @@ -0,0 +1,38 @@ +root: true + +env: + node: true + es6: true + es2017: true + +extends: standard + +parserOptions: + ecmaVersion: 2020 + +rules: + array-bracket-spacing: + - error + - always + array-callback-return: error + comma-dangle: + - error + - always-multiline + eqeqeq: + - error + - smart + guard-for-in: error + no-multiple-empty-lines: + - error + - + max: 2 + maxEOF: 0 + no-return-assign: error + no-useless-concat: error + space-before-function-paren: + - error + - never + quotes: + - error + - double + - avoid-escape diff --git a/package.json b/package.json index eb78fe4..5903645 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,9 @@ "scripts": { "build": "babel src --out-dir lib", "prepublish": "npm run build", - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "lint": "eslint src/", + "lint:fix": "eslint --fix src/" }, "repository": { "type": "git", @@ -48,6 +50,8 @@ }, "devDependencies": { "babel-cli": "^6.8.0", - "babel-preset-es2015": "^6.6.0" + "babel-preset-es2015": "^6.6.0", + "eslint": "^7.1.0", + "eslint-config-standard": "^14.1.1" } } diff --git a/src/bundle.js b/src/bundle.js index 3742124..3460671 100644 --- a/src/bundle.js +++ b/src/bundle.js @@ -1,59 +1,57 @@ -'use strict'; +"use strict" -const path = require('path'); +const path = require("path") -const Promise = require('bluebird') +const Promise = require("bluebird") -const fs = Promise.promisifyAll(require('fs')) +const fs = Promise.promisifyAll(require("fs")) -const spawn = require('cross-spawn') +const spawn = require("cross-spawn") -const debug = require('debug')('purs-loader'); +const debug = require("debug")("purs-loader") -const dargs = require('./dargs'); +const dargs = require("./dargs") module.exports = function bundle(options, bundleModules) { const stdout = [] const stderr = [] - const bundleCommand = options.pscBundle || 'purs'; + const bundleCommand = options.pscBundle || "purs" - const bundleArgs = (options.pscBundle ? [] : [ 'bundle' ]).concat(dargs(Object.assign({ - _: [path.join(options.output, '*', '*.js')], + const bundleArgs = (options.pscBundle ? [] : [ "bundle" ]).concat(dargs(Object.assign({ + _: [ path.join(options.output, "*", "*.js") ], output: options.bundleOutput, namespace: options.bundleNamespace, - }, options.pscBundleArgs))); + }, options.pscBundleArgs))) - bundleModules.forEach(name => bundleArgs.push('--module', name)) + bundleModules.forEach(name => bundleArgs.push("--module", name)) - debug('bundle: %s %O', bundleCommand, bundleArgs); + debug("bundle: %s %O", bundleCommand, bundleArgs) return (new Promise((resolve, reject) => { - debug('bundling PureScript...') + debug("bundling PureScript...") const compilation = spawn(bundleCommand, bundleArgs) - compilation.stdout.on('data', data => stdout.push(data.toString())) + compilation.stdout.on("data", data => stdout.push(data.toString())) - compilation.stderr.on('data', data => stderr.push(data.toString())) + compilation.stderr.on("data", data => stderr.push(data.toString())) - compilation.on('close', code => { - debug('finished bundling PureScript.') + compilation.on("close", code => { + debug("finished bundling PureScript.") if (code !== 0) { - const errorMessage = stderr.join(''); + const errorMessage = stderr.join("") if (errorMessage.length) { reject(new Error(`bundling failed: ${errorMessage}`)) + } else { + reject(new Error("bundling failed")) } - else { - reject(new Error('bundling failed')) - } - } - else { + } else { resolve(fs.appendFileAsync(options.bundleOutput, `module.exports = ${options.bundleNamespace}`)) } }) })) -}; +} diff --git a/src/compile.js b/src/compile.js index 6759bf3..2e847ca 100644 --- a/src/compile.js +++ b/src/compile.js @@ -1,65 +1,64 @@ -'use strict'; +"use strict" -const Promise = require('bluebird'); +const Promise = require("bluebird") -const spawn = require('cross-spawn'); +const spawn = require("cross-spawn") -const debug_ = require('debug'); +const debug_ = require("debug") -const debug = debug_('purs-loader'); +const debug = debug_("purs-loader") -const debugVerbose = debug_('purs-loader:verbose'); +const debugVerbose = debug_("purs-loader:verbose") -const dargs = require('./dargs'); +const dargs = require("./dargs") module.exports = function compile(psModule) { const options = psModule.options - const compileCommand = options.psc || 'purs'; + const compileCommand = options.psc || "purs" - const compileArgs = (options.psc ? [] : [ 'compile' ]).concat(dargs(Object.assign({ + const compileArgs = (options.psc ? [] : [ "compile" ]).concat(dargs(Object.assign({ _: options.src, output: options.output, }, options.pscArgs))) - const stderr = []; + const stderr = [] - debug('compile %s %O', compileCommand, compileArgs) + debug("compile %s %O", compileCommand, compileArgs) return new Promise((resolve, reject) => { - debug('compiling PureScript...') + debug("compiling PureScript...") const compilation = spawn(compileCommand, compileArgs) - compilation.stderr.on('data', data => { - stderr.push(data.toString()); - }); + compilation.stderr.on("data", data => { + stderr.push(data.toString()) + }) - compilation.stdout.on('data', data => { - debugVerbose(data.toString()); - }); + compilation.stdout.on("data", data => { + debugVerbose(data.toString()) + }) - compilation.on('close', code => { - debug('finished compiling PureScript.') + compilation.on("close", code => { + debug("finished compiling PureScript.") if (code !== 0) { - const errorMessage = stderr.join(''); + const errorMessage = stderr.join("") if (errorMessage.length) { - psModule.emitError(errorMessage); + psModule.emitError(errorMessage) } if (options.watch) { - resolve(psModule); - } - else { - reject(new Error('compilation failed')) + resolve(psModule) + } else { + reject(new Error("compilation failed")) } } else { - const warningMessage = stderr.join(''); + const warningMessage = stderr.join("") if (options.warnings && warningMessage.length) { - psModule.emitWarning(warningMessage); + psModule.emitWarning(warningMessage) } resolve(psModule) } }) - }); -}; + }) +} diff --git a/src/dargs.js b/src/dargs.js index de049ba..81adb7a 100644 --- a/src/dargs.js +++ b/src/dargs.js @@ -1,7 +1,7 @@ -'use strict'; +"use strict" -const dargs = require('dargs'); +const dargs = require("dargs") -module.exports = function(obj){ - return dargs(obj, {ignoreFalse: true}); -}; +module.exports = function(obj) { + return dargs(obj, { ignoreFalse: true }) +} diff --git a/src/ide.js b/src/ide.js index d3707a4..6c34d25 100644 --- a/src/ide.js +++ b/src/ide.js @@ -1,85 +1,80 @@ -'use strict'; +"use strict" -const path = require('path'); +const path = require("path") -const Promise = require('bluebird'); +const Promise = require("bluebird") -const fs = Promise.promisifyAll(require('fs')); +const fs = Promise.promisifyAll(require("fs")) -const retryPromise = require('promise-retry'); +const retryPromise = require("promise-retry") -const spawn = require('cross-spawn'); +const spawn = require("cross-spawn") -const colors = require('chalk'); +const colors = require("chalk") -const debug_ = require('debug'); +const debug_ = require("debug") -const debug = debug_('purs-loader'); +const debug = debug_("purs-loader") -const debugVerbose = debug_('purs-loader:verbose'); +const debugVerbose = debug_("purs-loader:verbose") -const dargs = require('./dargs'); - -const compile = require('./compile'); - -const PsModuleMap = require('./purs-module-map'); +const dargs = require("./dargs") function UnknownModuleError() { - this.name = 'UnknownModuleError'; - this.stack = (new Error()).stack; + this.name = "UnknownModuleError" + this.stack = (new Error()).stack } -UnknownModuleError.prototype = Object.create(Error.prototype); +UnknownModuleError.prototype = Object.create(Error.prototype) -UnknownModuleError.prototype.constructor = UnknownModuleError; +UnknownModuleError.prototype.constructor = UnknownModuleError -module.exports.UnknownModuleError = UnknownModuleError; +module.exports.UnknownModuleError = UnknownModuleError function spawnIdeClient(body, options) { - const ideClientCommand = options.pscIdeClient || 'purs'; + const ideClientCommand = options.pscIdeClient || "purs" - const ideClientArgs = (options.pscIdeClient ? [] : ['ide', 'client']).concat(dargs(options.pscIdeClientArgs)); + const ideClientArgs = (options.pscIdeClient ? [] : [ "ide", "client" ]).concat(dargs(options.pscIdeClientArgs)) - const stderr = []; + const stderr = [] - const stdout = []; + const stdout = [] - debug('ide client %s %o %O', ideClientCommand, ideClientArgs, body); + debug("ide client %s %o %O", ideClientCommand, ideClientArgs, body) return new Promise((resolve, reject) => { - const ideClient = spawn(ideClientCommand, ideClientArgs); + const ideClient = spawn(ideClientCommand, ideClientArgs) - ideClient.stderr.on('data', data => { - stderr.push(data.toString()); + ideClient.stderr.on("data", data => { + stderr.push(data.toString()) }) - ideClient.stdout.on('data', data => { - stdout.push(data.toString()); + ideClient.stdout.on("data", data => { + stdout.push(data.toString()) }) - ideClient.on('close', code => { + ideClient.on("close", code => { if (code !== 0) { - const errorMessage = stderr.join(''); + const errorMessage = stderr.join("") - reject(new Error(`ide client failed: ${errorMessage}`)); - } - else { - const result = stdout.join(''); + reject(new Error(`ide client failed: ${errorMessage}`)) + } else { + const result = stdout.join("") - resolve(result); + resolve(result) } }) - ideClient.stdin.resume(); + ideClient.stdin.resume() - ideClient.stdin.write(JSON.stringify(body)); + ideClient.stdin.write(JSON.stringify(body)) - ideClient.stdin.write('\n'); - }); + ideClient.stdin.write("\n") + }) } function formatIdeResult(result, options, index, length) { - let numAndErr = `[${index+1}/${length} ${result.errorCode}]` + let numAndErr = `[${index + 1}/${length} ${result.errorCode}]` numAndErr = options.pscIdeColors ? colors.yellow(numAndErr) : numAndErr function makeResult() { @@ -87,14 +82,14 @@ function formatIdeResult(result, options, index, length) { } function makeResultSnippet(filename, pos) { - const srcPath = path.relative(options.context, filename); + const srcPath = path.relative(options.context, filename) const fileAndPos = `${srcPath}:${pos.startLine}:${pos.startColumn}` - return fs.readFileAsync(filename, 'utf8').then(source => { - const lines = source.split('\n').slice(pos.startLine - 1, pos.endLine) + return fs.readFileAsync(filename, "utf8").then(source => { + const lines = source.split("\n").slice(pos.startLine - 1, pos.endLine) const endsOnNewline = pos.endColumn === 1 && pos.startLine !== pos.endLine - const up = options.pscIdeColors ? colors.red('^') : '^' - const down = options.pscIdeColors ? colors.red('v') : 'v' + const up = options.pscIdeColors ? colors.red("^") : "^" + const down = options.pscIdeColors ? colors.red("v") : "v" let trimmed = lines.slice(0) if (endsOnNewline) { @@ -106,9 +101,9 @@ function formatIdeResult(result, options, index, length) { // strip newlines at the end if (endsOnNewline) { trimmed = lines.reverse().reduce((trimmed, line, i) => { - if (i === 0 && line === '') trimmed.trimming = true + if (i === 0 && line === "") trimmed.trimming = true if (!trimmed.trimming) trimmed.push(line) - if (trimmed.trimming && line !== '') { + if (trimmed.trimming && line !== "") { trimmed.trimming = false trimmed.push(line) } @@ -118,153 +113,149 @@ function formatIdeResult(result, options, index, length) { pos.endColumn = trimmed[trimmed.length - 1].length || 1 } - const spaces = ' '.repeat(String(pos.endLine).length) + const spaces = " ".repeat(String(pos.endLine).length) let snippet = trimmed.map((line, i) => { return ` ${pos.startLine + i} ${line}` - }).join('\n') + }).join("\n") if (trimmed.length === 1) { - snippet += `\n ${spaces} ${' '.repeat(pos.startColumn - 1)}${up.repeat(pos.endColumn - pos.startColumn + 1)}` + snippet += `\n ${spaces} ${" ".repeat(pos.startColumn - 1)}${up.repeat(pos.endColumn - pos.startColumn + 1)}` } else { - snippet = ` ${spaces} ${' '.repeat(pos.startColumn - 1)}${down}\n${snippet}` - snippet += `\n ${spaces} ${' '.repeat(pos.endColumn - 1)}${up}` + snippet = ` ${spaces} ${" ".repeat(pos.startColumn - 1)}${down}\n${snippet}` + snippet += `\n ${spaces} ${" ".repeat(pos.endColumn - 1)}${up}` } return Promise.resolve(`\n${numAndErr} ${fileAndPos}\n\n${snippet}\n\n${result.message}`) }).catch(error => { - debug('failed to format ide result: %o', error); + debug("failed to format ide result: %o", error) - return Promise.resolve(''); - }); + return Promise.resolve("") + }) } - return result.filename && result.position ? makeResultSnippet(result.filename, result.position) : makeResult(); + return result.filename && result.position ? makeResultSnippet(result.filename, result.position) : makeResult() } module.exports.connect = function connect(psModule) { const options = psModule.options - const serverCommand = options.pscIdeServer || 'purs'; + const serverCommand = options.pscIdeServer || "purs" - const serverArgs = (options.pscIdeServer ? [] : ['ide', 'server']).concat(dargs(Object.assign({ + const serverArgs = (options.pscIdeServer ? [] : [ "ide", "server" ]).concat(dargs(Object.assign({ outputDirectory: options.output, - '_': options.src - }, options.pscIdeServerArgs))); + _: options.src, + }, options.pscIdeServerArgs))) - debug('ide server: %s %o', serverCommand, serverArgs); + debug("ide server: %s %o", serverCommand, serverArgs) - const ideServer = spawn(serverCommand, serverArgs); + const ideServer = spawn(serverCommand, serverArgs) - ideServer.stdout.on('data', data => { - debugVerbose('ide server stdout: %s', data.toString()); - }); + ideServer.stdout.on("data", data => { + debugVerbose("ide server stdout: %s", data.toString()) + }) - ideServer.stderr.on('data', data => { - debugVerbose('ide server stderr: %s', data.toString()); - }); + ideServer.stderr.on("data", data => { + debugVerbose("ide server stderr: %s", data.toString()) + }) - ideServer.on('error', error => { - debugVerbose('ide server error: %o', error); - }); + ideServer.on("error", error => { + debugVerbose("ide server error: %o", error) + }) - ideServer.on('close', (code, signal) => { - debugVerbose('ide server close: %s %s', code, signal); - }); + ideServer.on("close", (code, signal) => { + debugVerbose("ide server close: %s %s", code, signal) + }) - return Promise.resolve(ideServer); -}; + return Promise.resolve(ideServer) +} module.exports.load = function load(psModule) { const options = psModule.options - const body = {command: 'load'}; + const body = { command: "load" } - return spawnIdeClient(body, options); -}; + return spawnIdeClient(body, options) +} module.exports.loadWithRetry = function loadWithRetry(psModule) { - const retries = 9; + const retries = 9 return retryPromise((retry, number) => { - debugVerbose('attempting to load modules (%d out of %d attempts)', number, retries); + debugVerbose("attempting to load modules (%d out of %d attempts)", number, retries) - return module.exports.load(psModule).catch(retry); + return module.exports.load(psModule).catch(retry) }, { retries: retries, factor: 1, minTimeout: 333, maxTimeout: 333, - }).then(() => psModule); -}; + }).then(() => psModule) +} module.exports.rebuild = function rebuild(psModule) { - const options = psModule.options; + const options = psModule.options const body = { - command: 'rebuild', + command: "rebuild", params: Object.assign({ file: psModule.srcPath, - }, options.pscIdeRebuildArgs) - }; + }, options.pscIdeRebuildArgs), + } const parseResponse = response => { try { - const parsed = JSON.parse(response); + const parsed = JSON.parse(response) - debugVerbose('parsed JSON response: %O', parsed); + debugVerbose("parsed JSON response: %O", parsed) - return Promise.resolve(parsed); - } - catch (error) { - return Promise.reject(error); + return Promise.resolve(parsed) + } catch (error) { + return Promise.reject(error) } - }; + } const formatResponse = parsed => { - const result = Array.isArray(parsed.result) ? parsed.result : []; + const result = Array.isArray(parsed.result) ? parsed.result : [] return Promise.map(result, (item, i) => { - debugVerbose('formatting result %O', item); + debugVerbose("formatting result %O", item) - return formatIdeResult(item, options, i, result.length); + return formatIdeResult(item, options, i, result.length) }).then(formatted => ({ parsed: parsed, formatted: formatted, - formattedMessage: formatted.join('\n') - })); - }; + formattedMessage: formatted.join("\n"), + })) + } return spawnIdeClient(body, options) .then(parseResponse) .then(formatResponse) .then(({ parsed, formatted, formattedMessage }) => { - if (parsed.resultType === 'success') { + if (parsed.resultType === "success") { if (options.warnings && formattedMessage.length) { - psModule.emitWarning(formattedMessage); + psModule.emitWarning(formattedMessage) } - return psModule; - } - else if ((parsed.result || []).some(item => { - const isModuleNotFound = item.errorCode === 'ModuleNotFound'; + return psModule + } else if ((parsed.result || []).some(item => { + const isModuleNotFound = item.errorCode === "ModuleNotFound" - const isUnknownModule = item.errorCode === 'UnknownModule'; + const isUnknownModule = item.errorCode === "UnknownModule" - const isUnknownModuleImport = item.errorCode === 'UnknownName' && /Unknown module/.test(item.message); + const isUnknownModuleImport = item.errorCode === "UnknownName" && /Unknown module/.test(item.message) - return isModuleNotFound || isUnknownModule || isUnknownModuleImport; + return isModuleNotFound || isUnknownModule || isUnknownModuleImport })) { - debug('module %s was not rebuilt because the module is unknown', psModule.name); + debug("module %s was not rebuilt because the module is unknown", psModule.name) - return Promise.reject(new UnknownModuleError()); - } - else { + return Promise.reject(new UnknownModuleError()) + } else { if (formattedMessage.length) { - psModule.emitError(formattedMessage); + psModule.emitError(formattedMessage) } - return psModule; + return psModule } }) - ; -}; +} diff --git a/src/index.js b/src/index.js index ff13302..c70999f 100644 --- a/src/index.js +++ b/src/index.js @@ -1,32 +1,32 @@ -'use strict' +"use strict" -const debug_ = require('debug'); +const debug_ = require("debug") -const debug = debug_('purs-loader'); +const debug = debug_("purs-loader") -const debugVerbose = debug_('purs-loader:verbose'); +const debugVerbose = debug_("purs-loader:verbose") -const loaderUtils = require('loader-utils') +const loaderUtils = require("loader-utils") -const Promise = require('bluebird') +const Promise = require("bluebird") -const path = require('path') +const path = require("path") -const PsModuleMap = require('./purs-module-map'); +const PsModuleMap = require("./purs-module-map") -const compile = require('./compile'); +const compile = require("./compile") -const bundle = require('./bundle'); +const bundle = require("./bundle") -const ide = require('./ide'); +const ide = require("./ide") -const toJavaScript = require('./to-javascript'); +const toJavaScript = require("./to-javascript") -const sourceMaps = require('./source-maps'); +const sourceMaps = require("./source-maps") -const spawn = require('cross-spawn').sync +const spawn = require("cross-spawn").sync -const eol = require('os').EOL +const eol = require("os").EOL var CACHE_VAR = { rebuild: false, @@ -40,106 +40,99 @@ var CACHE_VAR = { compilationFinished: false, installed: false, srcOption: [], - spagoOutputPath: null -}; + spagoOutputPath: null, +} // include src files provided by psc-package or Spago function requestDependencySources(packagerCommand, srcPath, loaderOptions) { - const packagerArgs = ['sources']; + const packagerArgs = [ "sources" ] const loaderSrc = loaderOptions.src || [ - srcPath - ]; + srcPath, + ] - debug('%s %o', packagerCommand, packagerArgs); + debug("%s %o", packagerCommand, packagerArgs) - const cmd = spawn(packagerCommand, packagerArgs); + const cmd = spawn(packagerCommand, packagerArgs) if (cmd.error) { - throw new Error(cmd.error); - } - else if (cmd.status !== 0) { - const error = cmd.stdout.toString(); + throw new Error(cmd.error) + } else if (cmd.status !== 0) { + const error = cmd.stdout.toString() - throw new Error(error); - } - else { - const result = cmd.stdout.toString().split(eol).filter(v => v != '').concat(loaderSrc); + throw new Error(error) + } else { + const result = cmd.stdout.toString().split(eol).filter(v => v !== "").concat(loaderSrc) - debug('%s result: %o', packagerCommand, result); + debug("%s result: %o", packagerCommand, result) - CACHE_VAR.srcOption = result; + CACHE_VAR.srcOption = result - return result; + return result } } // 'spago output path' will return the output folder in a monorepo function getSpagoSources() { - const cachedVal = CACHE_VAR.spagoOutputPath; + const cachedVal = CACHE_VAR.spagoOutputPath if (cachedVal) { return cachedVal } const command = "spago" - const args = ["path", "output"] + const args = [ "path", "output" ] - const cmd = spawn(command, args); + const cmd = spawn(command, args) if (cmd.error) { - throw new Error(cmd.error); - } - else if (cmd.status !== 0) { - const error = cmd.stdout.toString(); + throw new Error(cmd.error) + } else if (cmd.status !== 0) { + const error = cmd.stdout.toString() - throw new Error(error); - } - else { + throw new Error(error) + } else { const result = cmd.stdout.toString().split(eol)[0] - debug('"spago path output" result: %o', result); + debug('"spago path output" result: %o', result) - CACHE_VAR.spagoOutputPath = result; + CACHE_VAR.spagoOutputPath = result - return result; + return result } } module.exports = function purescriptLoader(source, map) { - this.cacheable && this.cacheable(); + this.cacheable && this.cacheable() - const webpackContext = (this.options && this.options.context) || this.rootContext; + const webpackContext = (this.options && this.options.context) || this.rootContext - const callback = this.async(); + const callback = this.async() - const loaderOptions = loaderUtils.getOptions(this) || {}; + const loaderOptions = loaderUtils.getOptions(this) || {} const srcOption = ((pscPackage, spago) => { - const srcPath = path.join('src', '**', '*.purs'); + const srcPath = path.join("src", "**", "*.purs") - const bowerPath = path.join('bower_components', 'purescript-*', 'src', '**', '*.purs'); + const bowerPath = path.join("bower_components", "purescript-*", "src", "**", "*.purs") if (CACHE_VAR.srcOption.length > 0) { - return CACHE_VAR.srcOption; - } - else if (pscPackage) { - return requestDependencySources('psc-package', srcPath, loaderOptions) - } - else if (spago) { - return requestDependencySources('spago', srcPath, loaderOptions) - } - else { + return CACHE_VAR.srcOption + } else if (pscPackage) { + return requestDependencySources("psc-package", srcPath, loaderOptions) + } else if (spago) { + return requestDependencySources("spago", srcPath, loaderOptions) + } else { const result = loaderOptions.src || [ bowerPath, - srcPath - ]; + srcPath, + ] - CACHE_VAR.srcOption = result; + CACHE_VAR.srcOption = result - return result; + return result } - })(loaderOptions.pscPackage, loaderOptions.spago); - - const outputPath = loaderOptions.spago ? getSpagoSources() : 'output' + })(loaderOptions.pscPackage, loaderOptions.spago) + + const outputPath = loaderOptions.spago ? getSpagoSources() : "output" const options = Object.assign({ context: webpackContext, @@ -153,27 +146,27 @@ module.exports = function purescriptLoader(source, map) { pscIdeServerArgs: {}, pscIdeRebuildArgs: {}, pscIde: false, - pscIdeColors: loaderOptions.psc === 'psa', + pscIdeColors: loaderOptions.psc === "psa", pscPackage: false, spago: false, - bundleOutput: 'output/bundle.js', - bundleNamespace: 'PS', + bundleOutput: "output/bundle.js", + bundleNamespace: "PS", bundle: false, warnings: true, watch: false, output: outputPath, - src: [] + src: [], }, loaderOptions, { - src: srcOption - }); + src: srcOption, + }) if (!CACHE_VAR.installed) { - debugVerbose('installing purs-loader with options: %O', options); + debugVerbose("installing purs-loader with options: %O", options) - CACHE_VAR.installed = true; + CACHE_VAR.installed = true const invalidCb = () => { - debugVerbose('invalidating loader CACHE_VAR'); + debugVerbose("invalidating loader CACHE_VAR") CACHE_VAR = { rebuild: options.pscIde, @@ -186,198 +179,189 @@ module.exports = function purescriptLoader(source, map) { compilationStarted: false, compilationFinished: false, installed: CACHE_VAR.installed, - srcOption: [] - }; + srcOption: [], + } } // invalidate loader CACHE_VAR when bundle is marked as invalid (in watch mode) - if(this._compiler.hooks){ - this._compiler.hooks.invalid.tap('purs-loader', invalidCb); + if (this._compiler.hooks) { + this._compiler.hooks.invalid.tap("purs-loader", invalidCb) } else { - this._compiler.plugin('invalid', invalidCb); + this._compiler.plugin("invalid", invalidCb) } const afterCompileCb = (compilation, callback) => { CACHE_VAR.warnings.forEach(warning => { - compilation.warnings.push(warning); - }); + compilation.warnings.push(warning) + }) CACHE_VAR.errors.forEach(error => { - compilation.errors.push(error); - }); + compilation.errors.push(error) + }) callback() } // add psc warnings to webpack compilation warnings - if(this._compiler.hooks) { - this._compiler.hooks.afterCompile.tapAsync('purs-loader', afterCompileCb); + if (this._compiler.hooks) { + this._compiler.hooks.afterCompile.tapAsync("purs-loader", afterCompileCb) } else { - this._compiler.plugin('after-compile', afterCompileCb); + this._compiler.plugin("after-compile", afterCompileCb) } } - const psModuleName = PsModuleMap.matchModule(source); + const psModuleName = PsModuleMap.matchModule(source) const psModule = { name: psModuleName, source: source, - load: ({js, map}) => callback(null, js, map), + load: ({ js, map }) => callback(null, js, map), reject: error => callback(error), srcPath: this.resourcePath, remainingRequest: loaderUtils.getRemainingRequest(this), srcDir: path.dirname(this.resourcePath), - jsPath: path.resolve(path.join(options.output, psModuleName, 'index.js')), + jsPath: path.resolve(path.join(options.output, psModuleName, "index.js")), options: options, cache: CACHE_VAR, emitWarning: warning => { if (options.warnings && warning.length) { - CACHE_VAR.warnings.push(warning); + CACHE_VAR.warnings.push(warning) } }, emitError: error => { if (error.length) { - CACHE_VAR.errors.push(error); + CACHE_VAR.errors.push(error) } - } + }, } - debug('loading %s', psModule.name); + debug("loading %s", psModule.name) if (options.bundle) { - CACHE_VAR.bundleModules.push(psModule.name); + CACHE_VAR.bundleModules.push(psModule.name) } if (CACHE_VAR.rebuild) { const connect = () => { if (!CACHE_VAR.ideServer) { - CACHE_VAR.ideServer = true; + CACHE_VAR.ideServer = true return ide.connect(psModule) .then(ideServer => { - CACHE_VAR.ideServer = ideServer; - return psModule; + CACHE_VAR.ideServer = ideServer + return psModule }) .then(ide.loadWithRetry) .catch(error => { if (CACHE_VAR.ideServer.kill) { - debug('ide failed to initially load modules, stopping the ide server process'); + debug("ide failed to initially load modules, stopping the ide server process") - CACHE_VAR.ideServer.kill(); + CACHE_VAR.ideServer.kill() } - CACHE_VAR.ideServer = null; + CACHE_VAR.ideServer = null - return Promise.reject(error); + return Promise.reject(error) }) - ; + } else { + return Promise.resolve(psModule) } - else { - return Promise.resolve(psModule); - } - }; + } const rebuild = () => ide.rebuild(psModule) - .then(() => - toJavaScript(psModule) - .then(js => sourceMaps(psModule, js)) - .then(psModule.load) - .catch(psModule.reject) - ) - .catch(error => { - if (error instanceof ide.UnknownModuleError) { + .then(() => + toJavaScript(psModule) + .then(js => sourceMaps(psModule, js)) + .then(psModule.load) + .catch(psModule.reject), + ) + .catch(error => { + if (error instanceof ide.UnknownModuleError) { // Store the modules that trigger a recompile due to an // unknown module error. We need to wait until compilation is // done before loading these files. - CACHE_VAR.deferred.push(psModule); - - if (!CACHE_VAR.compilationStarted) { - CACHE_VAR.compilationStarted = true; - - return compile(psModule) - .then(() => { - CACHE_VAR.compilationFinished = true; - }) - .then(() => - Promise.map(CACHE_VAR.deferred, psModule => - ide.load(psModule) - .then(() => toJavaScript(psModule)) - .then(js => sourceMaps(psModule, js)) - .then(psModule.load) + CACHE_VAR.deferred.push(psModule) + + if (!CACHE_VAR.compilationStarted) { + CACHE_VAR.compilationStarted = true + + return compile(psModule) + .then(() => { + CACHE_VAR.compilationFinished = true + }) + .then(() => + Promise.map(CACHE_VAR.deferred, psModule => + ide.load(psModule) + .then(() => toJavaScript(psModule)) + .then(js => sourceMaps(psModule, js)) + .then(psModule.load), + ), ) - ) - .catch(error => { - CACHE_VAR.deferred[0].reject(error); + .catch(error => { + CACHE_VAR.deferred[0].reject(error) - CACHE_VAR.deferred.slice(1).forEach(psModule => { - psModule.reject(new Error('purs-loader failed')); + CACHE_VAR.deferred.slice(1).forEach(psModule => { + psModule.reject(new Error("purs-loader failed")) + }) }) - }) - ; - } - else { + } else { // The compilation has started. We must wait until it is // done in order to ensure the module map contains all of // the unknown modules. + } + } else { + debug("ide rebuild failed due to an unhandled error: %o", error) + + psModule.reject(error) } - } - else { - debug('ide rebuild failed due to an unhandled error: %o', error); + }) - psModule.reject(error); - } - }) - ; - connect().then(rebuild); - } - else if (CACHE_VAR.compilationFinished) { - debugVerbose('compilation is already finished, loading module %s', psModule.name); + connect().then(rebuild) + } else if (CACHE_VAR.compilationFinished) { + debugVerbose("compilation is already finished, loading module %s", psModule.name) toJavaScript(psModule) .then(js => sourceMaps(psModule, js)) .then(psModule.load) - .catch(psModule.reject); - } - else { + .catch(psModule.reject) + } else { // The compilation has not finished yet. We need to wait for // compilation to finish before the loaders run so that references // to compiled output are valid. Push the modules into the CACHE_VAR to // be loaded once the complation is complete. - CACHE_VAR.deferred.push(psModule); + CACHE_VAR.deferred.push(psModule) if (!CACHE_VAR.compilationStarted) { - CACHE_VAR.compilationStarted = true; + CACHE_VAR.compilationStarted = true compile(psModule) .then(() => { - CACHE_VAR.compilationFinished = true; + CACHE_VAR.compilationFinished = true }) .then(() => { if (options.bundle) { - return bundle(options, CACHE_VAR.bundleModules); + return bundle(options, CACHE_VAR.bundleModules) } }) .then(() => Promise.map(CACHE_VAR.deferred, psModule => toJavaScript(psModule) .then(js => sourceMaps(psModule, js)) - .then(psModule.load) - ) + .then(psModule.load), + ), ) .catch(error => { - CACHE_VAR.deferred[0].reject(error); + CACHE_VAR.deferred[0].reject(error) CACHE_VAR.deferred.slice(1).forEach(psModule => { - psModule.reject(new Error('purs-loader failed')); + psModule.reject(new Error("purs-loader failed")) }) }) - ; - } - else { + } else { // The complation has started. Nothing to do but wait until it is // done before loading all of the modules. } diff --git a/src/purs-module-map.js b/src/purs-module-map.js index b906d08..100279b 100644 --- a/src/purs-module-map.js +++ b/src/purs-module-map.js @@ -1,74 +1,74 @@ -'use strict'; +"use strict" -const path = require('path'); +const path = require("path") -const Promise = require('bluebird'); +const Promise = require("bluebird") -const fs = Promise.promisifyAll(require('fs')); +const fs = Promise.promisifyAll(require("fs")) -const globby = require('globby'); +const globby = require("globby") -const debug = require('debug')('purs-loader'); +const debug = require("debug")("purs-loader") -const srcModuleRegex = /(?:^|\n)module\s+([\w\.]+)/i; +const srcModuleRegex = /(?:^|\n)module\s+([\w.]+)/i -const importModuleRegex = /(?:^|\n)\s*import\s+([\w\.]+)/ig; +const importModuleRegex = /(?:^|\n)\s*import\s+([\w.]+)/ig module.exports.matchModule = function matchModule(str) { - const matches = str.match(srcModuleRegex); - return matches && matches[1]; -}; + const matches = str.match(srcModuleRegex) + return matches && matches[1] +} module.exports.matchImports = function matchImports(str) { - const matches = str.match(importModuleRegex); - return (matches || []).map(a => a.replace(/\n?\s*import\s+/i, '')); -}; + const matches = str.match(importModuleRegex) + return (matches || []).map(a => a.replace(/\n?\s*import\s+/i, "")) +} module.exports.makeMapEntry = function makeMapEntry(filePurs) { - const dirname = path.dirname(filePurs); + const dirname = path.dirname(filePurs) - const basename = path.basename(filePurs, '.purs'); + const basename = path.basename(filePurs, ".purs") - const fileJs = path.join(dirname, `${basename}.js`); + const fileJs = path.join(dirname, `${basename}.js`) const result = Promise.props({ - filePurs: fs.readFileAsync(filePurs, 'utf8'), - fileJs: fs.readFileAsync(fileJs, 'utf8').catch(() => undefined) + filePurs: fs.readFileAsync(filePurs, "utf8"), + fileJs: fs.readFileAsync(fileJs, "utf8").catch(() => undefined), }).then(fileMap => { - const sourcePurs = fileMap.filePurs; + const sourcePurs = fileMap.filePurs - const sourceJs = fileMap.fileJs; + const sourceJs = fileMap.fileJs - const moduleName = module.exports.matchModule(sourcePurs); + const moduleName = module.exports.matchModule(sourcePurs) - const imports = module.exports.matchImports(sourcePurs); + const imports = module.exports.matchImports(sourcePurs) - const map = {}; + const map = {} - map[moduleName] = map[moduleName] || {}; + map[moduleName] = map[moduleName] || {} - map[moduleName].src = path.resolve(filePurs); + map[moduleName].src = path.resolve(filePurs) - map[moduleName].imports = imports; + map[moduleName].imports = imports if (sourceJs) { - map[moduleName].ffi = path.resolve(fileJs); + map[moduleName].ffi = path.resolve(fileJs) } - return map; - }); + return map + }) - return result; -}; + return result +} module.exports.makeMap = function makeMap(src) { - debug('loading PureScript source and FFI files from %o', src); + debug("loading PureScript source and FFI files from %o", src) - const globs = [].concat(src); + const globs = [].concat(src) return globby(globs).then(paths => Promise.all(paths.map(module.exports.makeMapEntry)).then(result => - result.reduce(Object.assign, {}) - ) - ); -}; + result.reduce(Object.assign, {}), + ), + ) +} diff --git a/src/source-maps.js b/src/source-maps.js index 9e65867..77da772 100644 --- a/src/source-maps.js +++ b/src/source-maps.js @@ -1,68 +1,65 @@ -'use strict'; +"use strict" -const Promise = require('bluebird'); +const Promise = require("bluebird") -const fs = require('fs'); +const fs = require("fs") -const path = require('path'); +const path = require("path") -const debug_ = require('debug'); +const debug_ = require("debug") -const debugVerbose = debug_('purs-loader:verbose'); +const debugVerbose = debug_("purs-loader:verbose") module.exports = function sourceMap(psModule, js) { - const options = psModule.options; + const options = psModule.options - const jsPath = psModule.jsPath; + const jsPath = psModule.jsPath - const srcPath = psModule.srcPath; + const srcPath = psModule.srcPath - const source = psModule.source; + const source = psModule.source - const remainingRequest = psModule.remainingRequest; + const remainingRequest = psModule.remainingRequest - const sourceMapPath = path.join(path.dirname(jsPath), 'index.js.map'); + const sourceMapPath = path.join(path.dirname(jsPath), "index.js.map") - const isSourceMapsEnabled = options.pscArgs && options.pscArgs.sourceMaps; + const isSourceMapsEnabled = options.pscArgs && options.pscArgs.sourceMaps return new Promise((resolve, reject) => { if (!isSourceMapsEnabled) { resolve({ js: js, - map: undefined - }); - } - else { - debugVerbose('loading source map %s', sourceMapPath); + map: undefined, + }) + } else { + debugVerbose("loading source map %s", sourceMapPath) - fs.readFile(sourceMapPath, 'utf-8', (error, result) => { + fs.readFile(sourceMapPath, "utf-8", (error, result) => { if (error) { - reject(error); - } - else { + reject(error) + } else { try { const map = Object.assign(JSON.parse(result), { sources: [ - remainingRequest + remainingRequest, ], file: path.normalize(srcPath), sourcesContent: [ - source - ] - }); + source, + ], + }) - const jsRemovedMapUrl = js.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, '') + const jsRemovedMapUrl = js.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, "") resolve({ js: jsRemovedMapUrl, - map: map - }); - } - catch (error) { - reject(error); + map: map, + }) + } catch (error) { + reject(error) } } }) } - }); -}; + }) +} diff --git a/src/to-javascript.js b/src/to-javascript.js index 3663c83..be76792 100644 --- a/src/to-javascript.js +++ b/src/to-javascript.js @@ -1,65 +1,64 @@ -'use strict'; +"use strict" -const Promise = require('bluebird'); +const Promise = require("bluebird") -const fs = Promise.promisifyAll(require('fs')); +const fs = Promise.promisifyAll(require("fs")) -const path = require('path'); +const path = require("path") -const jsStringEscape = require('js-string-escape'); +const jsStringEscape = require("js-string-escape") -const difference = require('lodash.difference'); +const difference = require("lodash.difference") -const debug_ = require('debug'); +const debug_ = require("debug") -const debug = debug_('purs-loader'); +const debug = debug_("purs-loader") -const debugVerbose = debug_('purs-loader:verbose'); +const debugVerbose = debug_("purs-loader:verbose") -const PsModuleMap = require('./purs-module-map'); +const PsModuleMap = require("./purs-module-map") function updatePsModuleMap(psModule) { - const options = psModule.options; + const options = psModule.options - const cache = psModule.cache; + const cache = psModule.cache - const filePurs = psModule.srcPath; + const filePurs = psModule.srcPath if (!cache.psModuleMap) { - debugVerbose('module mapping does not exist - making a new module map'); + debugVerbose("module mapping does not exist - making a new module map") - cache.psModuleMap = PsModuleMap.makeMap(options.src); + cache.psModuleMap = PsModuleMap.makeMap(options.src) - return cache.psModuleMap; - } - else { - debugVerbose('module mapping exists - updating module map for %s', filePurs); + return cache.psModuleMap + } else { + debugVerbose("module mapping exists - updating module map for %s", filePurs) cache.psModuleMap = cache.psModuleMap.then(psModuleMap => PsModuleMap.makeMapEntry(filePurs).then(result => { - const map = Object.assign(psModuleMap, result); + const map = Object.assign(psModuleMap, result) - return map; - }) - ); + return map + }), + ) - return cache.psModuleMap; + return cache.psModuleMap } } - // Reference the bundle. +// Reference the bundle. function makeBundleJS(psModule) { - const bundleOutput = psModule.options.bundleOutput; + const bundleOutput = psModule.options.bundleOutput - const name = psModule.name; + const name = psModule.name - const srcDir = psModule.srcDir; + const srcDir = psModule.srcDir - const escaped = jsStringEscape(path.relative(srcDir, bundleOutput)); + const escaped = jsStringEscape(path.relative(srcDir, bundleOutput)) - const result = `module.exports = require("${escaped}")["${name}"]`; + const result = `module.exports = require("${escaped}")["${name}"]` - return Promise.resolve(result); + return Promise.resolve(result) } // Replace require paths to output files generated by psc with paths @@ -69,98 +68,95 @@ function makeBundleJS(psModule) { // any new imports in order to allow webpack to watch the new files // before they have been successfully compiled. function makeJS(psModule, psModuleMap, js) { - const requireRE = /require\(['"]\.\.\/([\w\.]+)(?:\/index\.js)?['"]\)/g; + const requireRE = /require\(['"]\.\.\/([\w.]+)(?:\/index\.js)?['"]\)/g - const foreignRE = /require\(['"]\.\/foreign(?:\.js)?['"]\)/g; + const foreignRE = /require\(['"]\.\/foreign(?:\.js)?['"]\)/g - const name = psModule.name; + const name = psModule.name - const imports = psModuleMap[name].imports; + const imports = psModuleMap[name].imports - var replacedImports = []; + var replacedImports = [] const result = js .replace(requireRE, (m, p1) => { - const moduleValue = psModuleMap[p1]; + const moduleValue = psModuleMap[p1] if (!moduleValue) { - debug('module %s was not found in the map, replacing require with null', p1); + debug("module %s was not found in the map, replacing require with null", p1) - return 'null'; - } - else { - const escapedPath = jsStringEscape(moduleValue.src); + return "null" + } else { + const escapedPath = jsStringEscape(moduleValue.src) - replacedImports.push(p1); + replacedImports.push(p1) - return `require("${escapedPath}")`; + return `require("${escapedPath}")` } }) .replace(foreignRE, () => { - const escapedPath = jsStringEscape(psModuleMap[name].ffi); + const escapedPath = jsStringEscape(psModuleMap[name].ffi) - return `require("${escapedPath}")`; + return `require("${escapedPath}")` }) - ; - const additionalImports = difference(imports, replacedImports); + + const additionalImports = difference(imports, replacedImports) if (!additionalImports.length) { - return Promise.resolve(result); - } - else { + return Promise.resolve(result) + } else { const missingImports = additionalImports.filter(moduleName => - !psModuleMap[moduleName] && moduleName.split('.')[0] !== 'Prim' - ); + !psModuleMap[moduleName] && moduleName.split(".")[0] !== "Prim", + ) - let updatingPsModuleMap; + let updatingPsModuleMap if (missingImports.length > 0) { - debug('rebuilding module map due to missing imports for %s: %o', name, missingImports); - psModule.cache.psModuleMap = null; - updatingPsModuleMap = updatePsModuleMap(psModule); + debug("rebuilding module map due to missing imports for %s: %o", name, missingImports) + psModule.cache.psModuleMap = null + updatingPsModuleMap = updatePsModuleMap(psModule) } else { - updatingPsModuleMap = Promise.resolve(psModuleMap); + updatingPsModuleMap = Promise.resolve(psModuleMap) } return updatingPsModuleMap.then(updatedPsModuleMap => { const missingImportsResult = missingImports.map(import_ => { - const moduleValue = updatedPsModuleMap[import_]; + const moduleValue = updatedPsModuleMap[import_] if (!moduleValue) { - debug('module %s was not found in the map, skipping require', import_); + debug("module %s was not found in the map, skipping require", import_) - return null; - } - else { - const escapedPath = jsStringEscape(moduleValue.src); + return null + } else { + const escapedPath = jsStringEscape(moduleValue.src) - return `var ${import_.replace(/\./g, '_')} = require("${escapedPath}")`; + return `var ${import_.replace(/\./g, "_")} = require("${escapedPath}")` } - }).filter(a => a !== null).join('\n'); + }).filter(a => a !== null).join("\n") - return result + '\n' + missingImportsResult; - }); + return result + "\n" + missingImportsResult + }) } } module.exports = function toJavaScript(psModule) { - const options = psModule.options; + const options = psModule.options - const cache = psModule.cache; + // const cache = psModule.cache - const bundlePath = path.resolve(options.bundleOutput); + const bundlePath = path.resolve(options.bundleOutput) - const jsPath = options.bundle ? bundlePath : psModule.jsPath; + const jsPath = options.bundle ? bundlePath : psModule.jsPath - const js = fs.readFileAsync(jsPath, 'utf8').catch(() => ''); + const js = fs.readFileAsync(jsPath, "utf8").catch(() => "") - const psModuleMap = updatePsModuleMap(psModule); + const psModuleMap = updatePsModuleMap(psModule) - debugVerbose('loading JavaScript for %s', psModule.name); + debugVerbose("loading JavaScript for %s", psModule.name) - return Promise.props({js: js, psModuleMap: psModuleMap}).then(result => - options.bundle ? - makeBundleJS(psModule) : - makeJS(psModule, result.psModuleMap, result.js) - ); -}; + return Promise.props({ js: js, psModuleMap: psModuleMap }).then(result => + options.bundle + ? makeBundleJS(psModule) + : makeJS(psModule, result.psModuleMap, result.js), + ) +}