Commit 29a0c3a1 authored by Robert Knight's avatar Robert Knight

Remove CoffeeScript support from build and test scripts

There is no CoffeeScript code left in the codebase so we don't need this
any more.
parent 67101824
......@@ -94,7 +94,7 @@ const appBundles = [
// annotation tools on the page and instantiating the sidebar application.
name: 'annotator',
entry: './src/annotator/index',
transforms: ['babel', 'coffee'],
transforms: ['babel'],
},
];
......
......@@ -26,7 +26,6 @@
"chance": "^1.0.13",
"classnames": "^2.2.4",
"codecov": "^3.1.0",
"coffeeify": "^2.1.0",
"commander": "^6.0.0",
"core-js": "^3.4.1",
"cross-env": "^7.0.0",
......
#!/usr/bin/env node
'use strict';
// A script to assist with CoffeeScript -> JS
// conversion.
//
// This script uses 'decaffeinate' to perform
// an initial CoffeeScript -> ES2015 conversion,
// then reformats the result using TypeScript formatter
// and JSCS
//
// The conversion process for H source is as follows:
//
// 1. Run this script on the .coffee file(s) to
// perform an initial conversion.
//
// If there is anything in the CoffeeScript that
// decaffeinate cannot handle, you might need to
// simplify the offending CoffeeScript (as indicated
// by the error output) and try again.
//
// 2. Remove the input .coffee files from the source tree.
// 3. Remove any ES2015-isms which are not currently allowed
// in the main codebase, also check for common issues
// in the converted source from the list below.
// 4. Re-run the tests and verify that everything works.
// 5. Repeat steps 1-4 with the '-test.coffee' file that
// corresponds to the converted source file.
//
// Issues to look out for in the converted source:
//
// - Run JSHint on the generated output and check for any
// violations (eg. unused variables)
// - Unnecessary 'return' statements in the last
// line of a function. CoffeeScript implicitly returns
// the last expression in a function, so the generated
// JS source has to do the same.
const Checker = require('jscs');
const babylon = require('babylon');
const decaffeinate = require('decaffeinate');
const fs = require('fs');
const path = require('path');
const typescriptFormatter = require('typescript-formatter');
const inFile = process.argv[2];
const jscsConfigPath = require.resolve('../../.jscsrc');
const jscsConfig = JSON.parse(fs.readFileSync(jscsConfigPath, 'utf-8'));
const stripReturnPatterns = [
// Unit test cases
/it\(/,
// Assignments in setters etc.
/[^=]+=[^=]+/,
];
/**
* Strip unnecessary 'return' statements from the last line of
* functions.
*
* In CoffeeScript, the last expression in a function is implicitly
* returned. Since the intended return type is unknown, 'decaffeinate'
* has to assume that the result _might_ be used.
*
* This function converts 'return <statement>' to '<statement>' for
* common cases in our codebase where we know that '<statement>'
* is not meant to be used as the return value.
*/
function stripUnnecessaryReturns(js) {
// This implementation is very stupid but works because we are
// only dealing with very simple expressions.
//
// If we need something more sophisticated, we shouldn't modify the
// source as a string but should instead write a Babel code transformer.
return js
.split('\n')
.map(line => {
const returnPrefix = 'return ';
if (line.trim().startsWith(returnPrefix)) {
const remainder = line.trim().slice(returnPrefix.length);
for (let i = 0; i < stripReturnPatterns.length; i++) {
if (remainder.match(stripReturnPatterns[i])) {
return remainder;
}
}
return line;
} else {
return line;
}
})
.join('\n');
}
/**
* Attempt to parse the input as ES2015 JS.
*
* @param {string} js - The input ES2015 JavaScript to parse.
* @throws {Error} An error with context information if the input JS
* cannot be parsed.
*/
function checkSyntax(js) {
try {
babylon.parse(js, { sourceType: 'module' });
} catch (err) {
const context = js
.split('\n')
.reduce((context, line, index) => {
const lineNumber = index + 1;
let linePrefix;
if (lineNumber === err.loc.line) {
linePrefix = `**${lineNumber}`;
} else {
linePrefix = ` ${lineNumber}`;
}
if (Math.abs(lineNumber - err.loc.line) < 10) {
return context.concat(`${linePrefix}: ${line}`);
} else {
return context;
}
}, [])
.join('\n');
throw new Error(
`Could not parsing ES2015 JavaScript generated by 'decaffeinate'.
You may need to fix or simplify the CoffeeScript first.
Error: ${err}
Context:\n${context}\n\n`
);
}
}
function reformat(js) {
// 1. Use Babylon (Babel's JS parser) to parse the generated JS
// and verify that it is syntactically valid.
// 2. Use typescript-formatter to do an initial pass over
// the code and correct indentation etc.
// 3. Finally, use JSCS to clean up smaller issues
js = `'use strict';\n\n${js}`;
js = stripUnnecessaryReturns(js);
try {
checkSyntax(js);
} catch (err) {
return Promise.reject(err);
}
return typescriptFormatter
.processString(inFile, js, {
baseDir: __dirname,
tsfmt: true,
})
.then(result => {
return result.dest;
})
.then(result => {
const checker = new Checker();
checker.configure(jscsConfig);
return checker.fixString(result).output;
});
}
function toResultOrError(promise) {
return promise
.then(result => {
return { result: result };
})
.catch(err => {
return { error: err };
});
}
function convertFile(inFile, outFile) {
console.log('Converting', inFile);
let js;
try {
js = decaffeinate.convert(fs.readFileSync(inFile).toString('utf8'));
} catch (err) {
return Promise.reject(err);
}
return reformat(js).then(result => {
fs.writeFileSync(outFile, result);
});
}
const conversions = [];
process.argv.slice(2).forEach(filePath => {
const inFile = path.resolve(filePath);
const outFile = inFile.replace(/\.coffee$/, '.js');
conversions.push(
toResultOrError(convertFile(inFile, outFile)).then(function (result) {
result.fileName = inFile;
return result;
})
);
});
Promise.all(conversions)
.then(results => {
let ok = 0;
let failed = 0;
results.forEach(result => {
if (result.error) {
console.log(
'Error converting %s: \n\n%s',
result.fileName,
result.error.message
);
++failed;
} else {
console.log('Converted %s', result.fileName);
++ok;
}
});
console.log('Converted %d files, failed to convert %d files', ok, failed);
})
.catch(err => {
console.log('Conversion error:', err);
});
{
"name": "decaf",
"version": "1.0.0",
"description": "Utility for converting CoffeeScript to JavaScript",
"main": "decaf.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"babylon": "^6.7.0",
"decaffeinate": "^1.44.13",
"jscs": "^2.11.0",
"typescript-formatter": "^1.2.0"
}
}
{
"indentSize": 2,
"tabSize": 2,
"newLineCharacter": "\n",
"convertTabsToSpaces": true,
"insertSpaceAfterCommaDelimiter": true,
"insertSpaceAfterSemicolonInForStatements": true,
"insertSpaceBeforeAndAfterBinaryOperators": true,
"insertSpaceAfterKeywordsInControlFlowStatements": true,
"insertSpaceAfterFunctionKeywordForAnonymousFunctions": true,
"insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis": false,
"placeOpenBraceOnNewLineForFunctions": false,
"placeOpenBraceOnNewLineForControlBlocks": false
}
......@@ -8,7 +8,6 @@ const path = require('path');
const babelify = require('babelify');
const browserify = require('browserify');
const coffeeify = require('coffeeify');
const exorcist = require('exorcist');
const log = require('fancy-log');
const envify = require('loose-envify/custom');
......@@ -27,25 +26,6 @@ function waitForever() {
return new Promise(function () {});
}
/**
* type Transform = 'coffee';
*
* interface BundleOptions {
* name: string;
* path: string;
*
* entry?: string[];
* require?: string[];
* transforms: Transform[];
*
* minify?: boolean;
* }
*
* interface BuildOptions {
* watch?: boolean;
* }
*/
/**
* Generates a JavaScript application or library bundle and source maps
* for debugging.
......@@ -71,7 +51,6 @@ module.exports = function createBundle(config, buildOpts) {
const bundleOpts = {
debug: true,
extensions: ['.coffee'],
// Browserify will try to detect and automatically provide
// browser implementations of Node modules.
......@@ -190,9 +169,6 @@ module.exports = function createBundle(config, buildOpts) {
bundle.external(config.external || []);
(config.transforms || []).forEach(function (transform) {
if (transform === 'coffee') {
bundle.transform(coffeeify);
}
if (transform === 'babel') {
bundle.transform(babelify);
}
......
......@@ -134,7 +134,7 @@ function drawHighlightsAbovePdfCanvas(highlightEl) {
}
/**
* Subset of the `NormalizedRange` class defined in `range.coffee` that this
* Subset of the `NormalizedRange` class defined in `range.js` that this
* module currently uses.
*
* @typedef NormalizedRange
......
......@@ -54,11 +54,7 @@ if (process.env.RUNNING_IN_DOCKER) {
}
module.exports = function (config) {
let testFiles = [
'annotator/**/*-test.coffee',
'**/test/*-test.js',
'**/integration/*-test.js',
];
let testFiles = ['**/test/*-test.js', '**/integration/*-test.js'];
if (config.grep) {
const allFiles = testFiles
......@@ -93,7 +89,6 @@ module.exports = function (config) {
// Disable watching because karma-browserify handles this.
watched: false,
// Configure `type` explicitly to avoid warning about .coffee files.
type: 'js',
})),
......@@ -114,27 +109,22 @@ module.exports = function (config) {
'./shared/polyfills/*.js': ['browserify'],
'./sidebar/test/bootstrap.js': ['browserify'],
'**/*-test.js': ['browserify'],
'**/*-test.coffee': ['browserify'],
'**/*-it.js': ['browserify'],
},
browserify: {
debug: true,
extensions: ['.coffee'],
transform: [
'coffeeify',
[
'babelify',
{
// The transpiled CoffeeScript is fed through Babelify to add
// code coverage instrumentation for Istanbul.
extensions: ['.js', '.coffee'],
extensions: ['.js'],
plugins: [
'mockable-imports',
[
'babel-plugin-istanbul',
{
exclude: ['**/test/**/*.{coffee,js}', '**/test-util/**'],
exclude: ['**/test/**/*.js', '**/test-util/**'],
},
],
],
......
......@@ -2339,20 +2339,6 @@ codecov@^3.1.0:
teeny-request "7.0.1"
urlgrey "0.4.4"
coffee-script@^1.12.0:
version "1.12.7"
resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.12.7.tgz#c05dae0cb79591d05b3070a8433a98c9a89ccc53"
integrity sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==
coffeeify@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/coffeeify/-/coffeeify-2.1.0.tgz#06de80aa87b6194937734361def4ade254b71a1e"
integrity sha1-Bt6Aqoe2GUk3c0Nh3vSt4lS3Gh4=
dependencies:
coffee-script "^1.12.0"
convert-source-map "^1.3.0"
through2 "^2.0.0"
collection-map@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/collection-map/-/collection-map-1.0.0.tgz#aea0f06f8d26c780c2b75494385544b2255af18c"
......@@ -2508,7 +2494,7 @@ content-type@~1.0.4:
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
convert-source-map@^1.1.0, convert-source-map@^1.1.3, convert-source-map@^1.3.0, convert-source-map@^1.5.0, convert-source-map@^1.7.0:
convert-source-map@^1.1.0, convert-source-map@^1.1.3, convert-source-map@^1.5.0, convert-source-map@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment