Unverified Commit 91608f05 authored by Robert Knight's avatar Robert Knight Committed by GitHub

Merge pull request #1135 from hypothesis/reformat-scripts

Format gulpfile.js and files in scripts/ using Prettier
parents ad1462f2 7883ae51
**/vendor/*
.*/**
build/
docs/
......@@ -68,22 +68,27 @@ function getEnv(key) {
}
/** A list of all modules included in vendor bundles. */
const vendorModules = Object.keys(vendorBundles.bundles)
.reduce(function (deps, key) {
return deps.concat(vendorBundles.bundles[key]);
}, []);
const vendorModules = Object.keys(vendorBundles.bundles).reduce(function(
deps,
key
) {
return deps.concat(vendorBundles.bundles[key]);
},
[]);
// Builds the bundles containing vendor JS code
gulp.task('build-vendor-js', function () {
gulp.task('build-vendor-js', function() {
const finished = [];
Object.keys(vendorBundles.bundles).forEach(function (name) {
finished.push(createBundle({
name: name,
require: vendorBundles.bundles[name],
minify: IS_PRODUCTION_BUILD,
path: SCRIPT_DIR,
noParse: vendorBundles.noParseModules,
}));
Object.keys(vendorBundles.bundles).forEach(function(name) {
finished.push(
createBundle({
name: name,
require: vendorBundles.bundles[name],
minify: IS_PRODUCTION_BUILD,
path: SCRIPT_DIR,
noParse: vendorBundles.noParseModules,
})
);
});
return Promise.all(finished);
});
......@@ -95,25 +100,29 @@ const appBundleBaseConfig = {
noParse: vendorBundles.noParseModules,
};
const appBundles = [{
// The entry point for both the Hypothesis client and the sidebar
// application. This is responsible for loading the rest of the assets needed
// by the client.
name: 'boot',
entry: './src/boot/index',
transforms: ['babel'],
},{
// The sidebar application for displaying and editing annotations.
name: 'sidebar',
transforms: ['babel'],
entry: './src/sidebar/index',
},{
// The annotation layer which handles displaying highlights, presenting
// annotation tools on the page and instantiating the sidebar application.
name: 'annotator',
entry: './src/annotator/index',
transforms: ['babel', 'coffee'],
}];
const appBundles = [
{
// The entry point for both the Hypothesis client and the sidebar
// application. This is responsible for loading the rest of the assets needed
// by the client.
name: 'boot',
entry: './src/boot/index',
transforms: ['babel'],
},
{
// The sidebar application for displaying and editing annotations.
name: 'sidebar',
transforms: ['babel'],
entry: './src/sidebar/index',
},
{
// The annotation layer which handles displaying highlights, presenting
// annotation tools on the page and instantiating the sidebar application.
name: 'annotator',
entry: './src/annotator/index',
transforms: ['babel', 'coffee'],
},
];
// Polyfill bundles. Polyfills are grouped into "sets" (one bundle per set)
// based on major ECMAScript version or DOM API. Some large polyfills
......@@ -142,23 +151,29 @@ const polyfillBundles = [
transforms: ['babel'],
}));
const appBundleConfigs = appBundles
.concat(polyfillBundles)
.map(config => {
return Object.assign({}, appBundleBaseConfig, config);
});
const appBundleConfigs = appBundles.concat(polyfillBundles).map(config => {
return Object.assign({}, appBundleBaseConfig, config);
});
gulp.task('build-js', gulp.parallel('build-vendor-js', function () {
return Promise.all(appBundleConfigs.map(function (config) {
return createBundle(config);
}));
}));
gulp.task(
'build-js',
gulp.parallel('build-vendor-js', function() {
return Promise.all(
appBundleConfigs.map(function(config) {
return createBundle(config);
})
);
})
);
gulp.task('watch-js', gulp.series('build-vendor-js', function watchJS() {
appBundleConfigs.forEach(function (config) {
createBundle(config, {watch: true});
});
}));
gulp.task(
'watch-js',
gulp.series('build-vendor-js', function watchJS() {
appBundleConfigs.forEach(function(config) {
createBundle(config, { watch: true });
});
})
);
const styleFiles = [
// H
......@@ -173,7 +188,7 @@ const styleFiles = [
'./node_modules/angular-toastr/dist/angular-toastr.css',
];
gulp.task('build-css', function () {
gulp.task('build-css', function() {
// Rewrite font URLs to look for fonts in 'build/fonts' instead of
// 'build/styles/fonts'
function rewriteCSSURL(asset) {
......@@ -188,7 +203,8 @@ gulp.task('build-css', function () {
url: rewriteCSSURL,
});
return gulp.src(styleFiles)
return gulp
.src(styleFiles)
.pipe(sourcemaps.init())
.pipe(gulpIf(isSASSFile, sass(sassOpts).on('error', sass.logError)))
.pipe(postcss([require('autoprefixer'), cssURLRewriter]))
......@@ -196,47 +212,61 @@ gulp.task('build-css', function () {
.pipe(gulp.dest(STYLE_DIR));
});
gulp.task('watch-css', gulp.series('build-css', function watchCSS() {
const vendorCSS = styleFiles.filter(function (path) {
return path.endsWith('.css');
});
const styleFileGlobs = vendorCSS.concat('./src/styles/**/*.scss');
gulp.watch(styleFileGlobs, gulp.task('build-css'));
}));
gulp.task(
'watch-css',
gulp.series('build-css', function watchCSS() {
const vendorCSS = styleFiles.filter(function(path) {
return path.endsWith('.css');
});
const styleFileGlobs = vendorCSS.concat('./src/styles/**/*.scss');
gulp.watch(styleFileGlobs, gulp.task('build-css'));
})
);
const fontFiles = ['src/styles/vendor/fonts/*.woff',
'node_modules/katex/dist/fonts/*.woff',
'node_modules/katex/dist/fonts/*.woff2'];
const fontFiles = [
'src/styles/vendor/fonts/*.woff',
'node_modules/katex/dist/fonts/*.woff',
'node_modules/katex/dist/fonts/*.woff2',
];
gulp.task('build-fonts', function () {
return gulp.src(fontFiles)
gulp.task('build-fonts', function() {
return gulp
.src(fontFiles)
.pipe(changed(FONTS_DIR))
.pipe(gulp.dest(FONTS_DIR));
});
gulp.task('watch-fonts', gulp.series('build-fonts', function watchFonts() {
gulp.watch(fontFiles, gulp.task('build-fonts'));
}));
gulp.task(
'watch-fonts',
gulp.series('build-fonts', function watchFonts() {
gulp.watch(fontFiles, gulp.task('build-fonts'));
})
);
const imageFiles = 'src/images/**/*';
gulp.task('build-images', function () {
return gulp.src(imageFiles)
gulp.task('build-images', function() {
return gulp
.src(imageFiles)
.pipe(changed(IMAGES_DIR))
.pipe(gulp.dest(IMAGES_DIR));
});
gulp.task('watch-images', gulp.series('build-images', function watchImages() {
gulp.watch(imageFiles, gulp.task('build-images'));
}));
gulp.task(
'watch-images',
gulp.series('build-images', function watchImages() {
gulp.watch(imageFiles, gulp.task('build-images'));
})
);
gulp.task('watch-templates', function () {
gulp.watch(TEMPLATES_DIR + '/*.html', function (file) {
gulp.task('watch-templates', function() {
gulp.watch(TEMPLATES_DIR + '/*.html', function(file) {
liveReloadServer.notifyChanged([file.path]);
});
});
const MANIFEST_SOURCE_FILES = 'build/@(fonts|images|scripts|styles)/*.@(js|css|woff|jpg|png|svg)';
const MANIFEST_SOURCE_FILES =
'build/@(fonts|images|scripts|styles)/*.@(js|css|woff|jpg|png|svg)';
let prevManifest = {};
......@@ -245,12 +275,12 @@ let prevManifest = {};
* two versions of a manifest.
*/
function changedAssets(prevManifest, newManifest) {
return Object.keys(newManifest).filter(function (asset) {
return Object.keys(newManifest).filter(function(asset) {
return newManifest[asset] !== prevManifest[asset];
});
}
const debouncedLiveReload = debounce(function () {
const debouncedLiveReload = debounce(function() {
// Notify dev clients about the changed assets. Note: This currently has an
// issue that if CSS, JS and templates are all changed in quick succession,
// some of the assets might be empty/incomplete files that are still being
......@@ -292,15 +322,16 @@ let isFirstBuild = true;
function generateBootScript(manifest) {
const { version } = require('./package.json');
const defaultSidebarAppUrl = process.env.SIDEBAR_APP_URL ?
`${process.env.SIDEBAR_APP_URL}` : 'https://hypothes.is/app.html';
const defaultSidebarAppUrl = process.env.SIDEBAR_APP_URL
? `${process.env.SIDEBAR_APP_URL}`
: 'https://hypothes.is/app.html';
let defaultAssetRoot;
if (process.env.NODE_ENV === 'production') {
defaultAssetRoot = `https://cdn.hypothes.is/hypothesis/${version}/`;
} else {
const scheme = useSsl ? 'https': 'http';
const scheme = useSsl ? 'https' : 'http';
defaultAssetRoot = `${scheme}://${packageServerHostname()}:3001/hypothesis/${version}/`;
}
......@@ -310,13 +341,14 @@ function generateBootScript(manifest) {
isFirstBuild = false;
}
gulp.src('build/scripts/boot.bundle.js')
gulp
.src('build/scripts/boot.bundle.js')
.pipe(replace('__MANIFEST__', JSON.stringify(manifest)))
.pipe(replace('__ASSET_ROOT__', defaultAssetRoot))
.pipe(replace('__SIDEBAR_APP_URL__', defaultSidebarAppUrl))
// Strip sourcemap link. It will have been invalidated by the previous
// replacements and the bundle is so small that it isn't really valuable.
.pipe(replace(/^\/\/# sourceMappingURL=\S+$/m,''))
.pipe(replace(/^\/\/# sourceMappingURL=\S+$/m, ''))
.pipe(rename('boot.js'))
.pipe(gulp.dest('build/'));
}
......@@ -326,33 +358,36 @@ function generateBootScript(manifest) {
* URLs containing cache-busting query string parameters.
*/
function generateManifest() {
return gulp.src(MANIFEST_SOURCE_FILES)
.pipe(manifest({name: 'manifest.json'}))
.pipe(through.obj(function (file, enc, callback) {
// Trigger a reload of the client in the dev server at localhost:3000
const newManifest = JSON.parse(file.contents.toString());
const changed = changedAssets(prevManifest, newManifest);
prevManifest = newManifest;
triggerLiveReload(changed);
// Expand template vars in boot script bundle
generateBootScript(newManifest);
this.push(file);
callback();
}))
return gulp
.src(MANIFEST_SOURCE_FILES)
.pipe(manifest({ name: 'manifest.json' }))
.pipe(
through.obj(function(file, enc, callback) {
// Trigger a reload of the client in the dev server at localhost:3000
const newManifest = JSON.parse(file.contents.toString());
const changed = changedAssets(prevManifest, newManifest);
prevManifest = newManifest;
triggerLiveReload(changed);
// Expand template vars in boot script bundle
generateBootScript(newManifest);
this.push(file);
callback();
})
)
.pipe(gulp.dest('build/'));
}
gulp.task('watch-manifest', function () {
gulp.task('watch-manifest', function() {
gulp.watch(MANIFEST_SOURCE_FILES, { delay: 500 }, generateManifest);
});
gulp.task('serve-package', function () {
gulp.task('serve-package', function() {
servePackage(3001, packageServerHostname());
});
gulp.task('serve-live-reload', function () {
gulp.task('serve-live-reload', function() {
const LiveReloadServer = require('./scripts/gulp/live-reload-server');
const scheme = useSsl ? 'https' : 'http';
liveReloadServer = new LiveReloadServer(3000, {
......@@ -368,16 +403,19 @@ const buildAssets = gulp.parallel(
);
gulp.task('build', gulp.series(buildAssets, generateManifest));
gulp.task('watch', gulp.parallel(
'serve-package',
'serve-live-reload',
'watch-js',
'watch-css',
'watch-fonts',
'watch-images',
'watch-manifest',
'watch-templates'
));
gulp.task(
'watch',
gulp.parallel(
'serve-package',
'serve-live-reload',
'watch-js',
'watch-css',
'watch-fonts',
'watch-images',
'watch-manifest',
'watch-templates'
)
);
function runKarma(baseConfig, opts, done) {
// See https://github.com/karma-runner/karma-mocha#configuration
......@@ -393,34 +431,46 @@ function runKarma(baseConfig, opts, done) {
// be displayed when using a non-default reporter.
// See https://github.com/karma-runner/karma/pull/2220
const BaseReporter = require('karma/lib/reporters/base');
BaseReporter.decoratorFactory.$inject =
BaseReporter.decoratorFactory.$inject.map(dep =>
dep.replace('browserLogOptions', 'browserConsoleLogOptions'));
BaseReporter.decoratorFactory.$inject = BaseReporter.decoratorFactory.$inject.map(
dep => dep.replace('browserLogOptions', 'browserConsoleLogOptions')
);
const karma = require('karma');
new karma.Server(Object.assign({}, {
configFile: path.resolve(__dirname, baseConfig),
}, cliOpts, opts), done).start();
new karma.Server(
Object.assign(
{},
{
configFile: path.resolve(__dirname, baseConfig),
},
cliOpts,
opts
),
done
).start();
}
gulp.task('test', function (callback) {
runKarma('./src/karma.config.js', {singleRun:true}, callback);
gulp.task('test', function(callback) {
runKarma('./src/karma.config.js', { singleRun: true }, callback);
});
gulp.task('test-watch', function (callback) {
gulp.task('test-watch', function(callback) {
runKarma('./src/karma.config.js', {}, callback);
});
gulp.task('upload-sourcemaps', gulp.series('build-js', function () {
const uploadToSentry = require('./scripts/gulp/upload-to-sentry');
const opts = {
key: getEnv('SENTRY_API_KEY'),
organization: getEnv('SENTRY_ORGANIZATION'),
};
const projects = getEnv('SENTRY_PROJECTS').split(',');
const release = getEnv('SENTRY_RELEASE_VERSION');
return gulp.src(['build/scripts/*.js', 'build/scripts/*.map'])
.pipe(uploadToSentry(opts, projects, release));
}));
gulp.task(
'upload-sourcemaps',
gulp.series('build-js', function() {
const uploadToSentry = require('./scripts/gulp/upload-to-sentry');
const opts = {
key: getEnv('SENTRY_API_KEY'),
organization: getEnv('SENTRY_ORGANIZATION'),
};
const projects = getEnv('SENTRY_PROJECTS').split(',');
const release = getEnv('SENTRY_RELEASE_VERSION');
return gulp
.src(['build/scripts/*.js', 'build/scripts/*.map'])
.pipe(uploadToSentry(opts, projects, release));
})
);
......@@ -149,8 +149,8 @@
"scripts": {
"build": "cross-env NODE_ENV=production gulp build",
"lint": "eslint .",
"checkformatting": "prettier --check 'src/**/*.js'",
"format": "prettier --list-different --write 'src/**/*.js'",
"checkformatting": "prettier --check '**/*.js'",
"format": "prettier --list-different --write '**/*.js'",
"test": "gulp test",
"report-coverage": "codecov -f coverage/coverage-final.json",
"version": "make clean build/manifest.json",
......
......@@ -19,7 +19,9 @@ async function createGitHubRelease() {
const GITHUB_ORG_REPO_PAT = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/;
if (!pkg.repository || !pkg.repository.match(GITHUB_ORG_REPO_PAT)) {
throw new Error('package.json is missing a "repository" field of the form :owner/:repo');
throw new Error(
'package.json is missing a "repository" field of the form :owner/:repo'
);
}
if (!process.env.GITHUB_TOKEN) {
......
......@@ -74,20 +74,23 @@ function stripUnnecessaryReturns(js) {
//
// 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 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;
}
return line;
} else {
return line;
}
}).join('\n');
})
.join('\n');
}
/**
......@@ -99,28 +102,32 @@ function stripUnnecessaryReturns(js) {
*/
function checkSyntax(js) {
try {
babylon.parse(js, {sourceType: 'module'});
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');
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`);
Context:\n${context}\n\n`
);
}
}
......@@ -140,12 +147,14 @@ function reformat(js) {
return Promise.reject(err);
}
return typescriptFormatter.processString(inFile, js, {
baseDir: __dirname,
tsfmt: true,
}).then(result => {
return result.dest;
})
return typescriptFormatter
.processString(inFile, js, {
baseDir: __dirname,
tsfmt: true,
})
.then(result => {
return result.dest;
})
.then(result => {
const checker = new Checker();
checker.configure(jscsConfig);
......@@ -154,11 +163,13 @@ function reformat(js) {
}
function toResultOrError(promise) {
return promise.then(result => {
return { result: result };
}).catch(err => {
return { error: err };
});
return promise
.then(result => {
return { result: result };
})
.catch(err => {
return { error: err };
});
}
function convertFile(inFile, outFile) {
......@@ -167,9 +178,7 @@ function convertFile(inFile, outFile) {
let js;
try {
js = decaffeinate.convert(
fs.readFileSync(inFile).toString('utf8')
);
js = decaffeinate.convert(fs.readFileSync(inFile).toString('utf8'));
} catch (err) {
return Promise.reject(err);
}
......@@ -183,25 +192,33 @@ 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;
}));
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;
}
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);
});
console.log('Converted %d files, failed to convert %d files', ok, failed);
}).catch(err => {
console.log('Conversion error:', err);
});
......@@ -49,9 +49,11 @@ class S3Uploader {
async upload(destPath, srcFile, { cacheControl }) {
if (!this.region) {
// Find out where the S3 bucket is.
const regionResult = await this.s3.getBucketLocation({
Bucket: this.bucket,
}).promise();
const regionResult = await this.s3
.getBucketLocation({
Bucket: this.bucket,
})
.promise();
this.region = regionResult.LocationConstraint;
this.s3 = new AWS.S3({ region: this.region });
}
......@@ -101,20 +103,16 @@ async function uploadPackageToS3(bucket, options) {
// Upload all package files to `$PACKAGE_NAME/$VERSION`.
const uploads = files.map(file =>
uploader.upload(
`${packageName}/${version}/${file}`,
file,
{ cacheControl: cacheForever },
)
uploader.upload(`${packageName}/${version}/${file}`, file, {
cacheControl: cacheForever,
})
);
await Promise.all(uploads);
// Upload a copy of the entry-point to `$PACKAGE_NAME@$VERSION`.
await uploader.upload(
`${packageName}@${version}`,
entryPoint,
{ cacheControl: cacheForever },
);
await uploader.upload(`${packageName}@${version}`, entryPoint, {
cacheControl: cacheForever,
});
// Upload a copy of the entry-point to `$PACKAGE_NAME` or `$PACKAGE_NAME@$TAG`.
// This enables creating URLs that always point to the current version of
......@@ -151,8 +149,7 @@ const options = {
cacheEntry: commander.cacheEntry,
};
uploadPackageToS3(commander.bucket, options)
.catch(err => {
console.error('Failed to upload S3 package', err);
process.exit(1);
});
uploadPackageToS3(commander.bucket, options).catch(err => {
console.error('Failed to upload S3 package', err);
process.exit(1);
});
......@@ -20,14 +20,14 @@ const watchify = require('watchify');
const log = gulpUtil.log;
function streamFinished(stream) {
return new Promise(function (resolve, reject) {
return new Promise(function(resolve, reject) {
stream.on('finish', resolve);
stream.on('error', reject);
});
}
function waitForever() {
return new Promise(function () {});
return new Promise(function() {});
}
/**
......@@ -38,22 +38,25 @@ function waitForever() {
* @param {string} trailerCode - Code added at the end of the wrapper function.
* @return {Transform} - A Node `Transform` stream.
*/
function wrapCodeWithFunction(headerCode, trailerCode='') {
function wrapCodeWithFunction(headerCode, trailerCode = '') {
const iifeStart = '(function() {' + headerCode + ';';
const iifeEnd = ';' + trailerCode + '})()';
let isFirstChunk = true;
return through(function (data, enc, callback) {
if (isFirstChunk) {
isFirstChunk = false;
this.push(Buffer.from(iifeStart));
return through(
function(data, enc, callback) {
if (isFirstChunk) {
isFirstChunk = false;
this.push(Buffer.from(iifeStart));
}
this.push(data);
callback();
},
function(callback) {
this.push(Buffer.from(iifeEnd));
callback();
}
this.push(data);
callback();
}, function (callback) {
this.push(Buffer.from(iifeEnd));
callback();
});
);
}
/**
......@@ -110,7 +113,7 @@ function useExternalRequireName(name) {
module.exports = function createBundle(config, buildOpts) {
mkdirp.sync(config.path);
buildOpts = buildOpts || {watch: false};
buildOpts = buildOpts || { watch: false };
// Use a custom name for the "require" function that bundles use to export
// and import modules from other bundles. This avoids conflicts with eg.
......@@ -133,11 +136,7 @@ module.exports = function createBundle(config, buildOpts) {
//
// See node_modules/browserify/lib/builtins.js to find out which
// modules provide the implementations of these.
builtins: [
'console',
'_process',
'querystring',
],
builtins: ['console', '_process', 'querystring'],
externalRequireName,
insertGlobalVars: {
// The Browserify polyfill for the `Buffer` global is large and
......@@ -151,7 +150,7 @@ module.exports = function createBundle(config, buildOpts) {
// `global` or `self` that is not an alias for `window`. See
// https://github.com/hypothesis/h/issues/2723 and
// https://github.com/hypothesis/client/issues/277
global: function () {
global: function() {
return 'window';
},
},
......@@ -165,7 +164,7 @@ module.exports = function createBundle(config, buildOpts) {
// Specify modules that Browserify should not parse.
// The 'noParse' array must contain full file paths,
// not module names.
bundleOpts.noParse = (config.noParse || []).map(function (id) {
bundleOpts.noParse = (config.noParse || []).map(function(id) {
// If package.json specifies a custom entry point for the module for
// use in the browser, resolve that.
const packageConfig = require('../../package.json');
......@@ -184,7 +183,7 @@ module.exports = function createBundle(config, buildOpts) {
const bundle = browserify([], bundleOpts);
(config.require || []).forEach(function (req) {
(config.require || []).forEach(function(req) {
// When another bundle uses 'bundle.external(<module path>)',
// the module path is rewritten relative to the
// base directory and a '/' prefix is added, so
......@@ -194,15 +193,17 @@ module.exports = function createBundle(config, buildOpts) {
// In the bundle which provides './dir/module', we
// therefore need to expose the module as '/dir/module'.
if (req[0] === '.') {
bundle.require(req, {expose: req.slice(1)});
bundle.require(req, { expose: req.slice(1) });
} else if (req[0] === '/') {
// If the require path is absolute, the same rules as
// above apply but the path needs to be relative to
// the root of the repository
const repoRootPath = path.join(__dirname, '../../');
const relativePath = path.relative(path.resolve(repoRootPath),
path.resolve(req));
bundle.require(req, {expose: '/' + relativePath});
const relativePath = path.relative(
path.resolve(repoRootPath),
path.resolve(req)
);
bundle.require(req, { expose: '/' + relativePath });
} else {
// this is a package under node_modules/, no
// rewriting required.
......@@ -213,7 +214,7 @@ module.exports = function createBundle(config, buildOpts) {
bundle.add(config.entry || []);
bundle.external(config.external || []);
(config.transforms || []).forEach(function (transform) {
(config.transforms || []).forEach(function(transform) {
if (transform === 'coffee') {
bundle.transform(coffeeify);
}
......@@ -223,49 +224,56 @@ module.exports = function createBundle(config, buildOpts) {
});
if (config.minify) {
bundle.transform({global: true}, uglifyify);
bundle.transform({ global: true }, uglifyify);
}
// Include or disable debugging checks in our code and dependencies by
// replacing references to `process.env.NODE_ENV`.
bundle.transform(envify({
NODE_ENV: process.env.NODE_ENV || 'development',
}), {
// Ideally packages should configure this transform in their package.json
// file if they need it, but not all of them do.
global: true,
});
bundle.transform(
envify({
NODE_ENV: process.env.NODE_ENV || 'development',
}),
{
// Ideally packages should configure this transform in their package.json
// file if they need it, but not all of them do.
global: true,
}
);
function build() {
const output = fs.createWriteStream(bundlePath);
const b = bundle.bundle();
b.on('error', function (err) {
b.on('error', function(err) {
log('Build error', err.toString());
});
const stream = (b
const stream = b
.pipe(useExternalRequireName(externalRequireName))
.pipe(exorcist(sourcemapPath))
.pipe(output));
.pipe(output);
return streamFinished(stream);
}
if (buildOpts.watch) {
bundle.plugin(watchify);
bundle.on('update', function (ids) {
bundle.on('update', function(ids) {
const start = Date.now();
log('Source files changed', ids);
build().then(function () {
log('Updated %s (%d ms)', bundleFileName, Date.now() - start);
}).catch(function (err) {
console.error('Building updated bundle failed:', err);
});
});
build().then(function () {
log('Built ' + bundleFileName);
}).catch(function (err) {
console.error('Error building bundle:', err);
build()
.then(function() {
log('Updated %s (%d ms)', bundleFileName, Date.now() - start);
})
.catch(function(err) {
console.error('Building updated bundle failed:', err);
});
});
build()
.then(function() {
log('Built ' + bundleFileName);
})
.catch(function(err) {
console.error('Error building bundle:', err);
});
return waitForever();
} else {
......
......@@ -42,7 +42,7 @@ function LiveReloadServer(port, config) {
function listen() {
const log = gulpUtil.log;
const app = function (req, response) {
const app = function(req, response) {
const url = urlParser.parse(req.url);
let content;
......@@ -140,7 +140,7 @@ function LiveReloadServer(port, config) {
};
const server = createServer(app);
server.listen(port, function (err) {
server.listen(port, function(err) {
if (err) {
log('Setting up live reload server failed', err);
}
......@@ -152,14 +152,14 @@ function LiveReloadServer(port, config) {
httpServer: server,
});
ws.on('request', function (req) {
ws.on('request', function(req) {
log('Live reload client connected');
const conn = req.accept(null, req.origin);
connections.push(conn);
conn.on('close', function () {
conn.on('close', function() {
const closedConn = conn;
connections = connections.filter(function (conn) {
connections = connections.filter(function(conn) {
return conn !== closedConn;
});
});
......@@ -173,12 +173,14 @@ function LiveReloadServer(port, config) {
* Paths are relative to the root asset
* build directory.
*/
this.notifyChanged = function (assets) {
connections.forEach(function (conn) {
conn.sendUTF(JSON.stringify({
type: 'assets-changed',
changed: assets,
}));
this.notifyChanged = function(assets) {
connections.forEach(function(conn) {
conn.sendUTF(
JSON.stringify({
type: 'assets-changed',
changed: assets,
})
);
});
};
......
......@@ -14,24 +14,27 @@ const VinylFile = require('vinyl');
* manifest mapping input paths (eg. "scripts/foo.js")
* to URLs with cache-busting query parameters (eg. "scripts/foo.js?af95bd").
*/
module.exports = function (opts) {
module.exports = function(opts) {
const manifest = {};
return through.obj(function (file, enc, callback) {
const hash = crypto.createHash('sha1');
hash.update(file.contents);
return through.obj(
function(file, enc, callback) {
const hash = crypto.createHash('sha1');
hash.update(file.contents);
const hashSuffix = hash.digest('hex').slice(0, 6);
const relPath = path.relative('build/', file.path);
manifest[relPath] = relPath + '?' + hashSuffix;
const hashSuffix = hash.digest('hex').slice(0, 6);
const relPath = path.relative('build/', file.path);
manifest[relPath] = relPath + '?' + hashSuffix;
callback();
}, function (callback) {
const manifestFile = new VinylFile({
path: opts.name,
contents: Buffer.from(JSON.stringify(manifest, null, 2), 'utf-8'),
});
this.push(manifestFile);
callback();
});
callback();
},
function(callback) {
const manifestFile = new VinylFile({
path: opts.name,
contents: Buffer.from(JSON.stringify(manifest, null, 2), 'utf-8'),
});
this.push(manifestFile);
callback();
}
);
};
......@@ -23,13 +23,13 @@ function servePackage(port, hostname) {
const app = express();
// Enable CORS for assets so that cross-origin font loading works.
app.use(function (req, res, next) {
app.use(function(req, res, next) {
res.append('Access-Control-Allow-Origin', '*');
res.append('Access-Control-Allow-Methods', 'GET');
next();
});
const serveBootScript = function (req, res) {
const serveBootScript = function(req, res) {
const entryPath = require.resolve('../..');
const entryScript = readFileSync(entryPath).toString('utf-8');
res.send(entryScript);
......
......@@ -17,11 +17,10 @@ const SENTRY_API_ROOT = 'https://app.getsentry.com/api/0';
* }
*/
/** Wrapper around request() that returns a Promise. */
function httpRequest(opts) {
return new Promise(function (resolve, reject) {
request(opts, function (err, response, body) {
return new Promise(function(resolve, reject) {
request(opts, function(err, response, body) {
if (err) {
reject(err);
} else {
......@@ -34,11 +33,12 @@ function httpRequest(opts) {
});
}
/** Create a release in Sentry. Returns a Promise. */
function createRelease(opts, project, release) {
return httpRequest({
uri: `${SENTRY_API_ROOT}/projects/${opts.organization}/${project}/releases/`,
uri: `${SENTRY_API_ROOT}/projects/${
opts.organization
}/${project}/releases/`,
method: 'POST',
auth: {
user: opts.key,
......@@ -48,23 +48,27 @@ function createRelease(opts, project, release) {
version: release,
},
json: true,
}).then(function (result) {
const success = (result.response.statusCode === 201);
const alreadyCreated = (result.response.statusCode === 400 &&
result.body.detail.match(/already exists/));
}).then(function(result) {
const success = result.response.statusCode === 201;
const alreadyCreated =
result.response.statusCode === 400 &&
result.body.detail.match(/already exists/);
if (success || alreadyCreated) {
return;
}
throw new Error(`unable to create release '${release}' in project '${project}'`);
throw new Error(
`unable to create release '${release}' in project '${project}'`
);
});
}
/** Upload a named file to a release in Sentry. Returns a Promise. */
function uploadReleaseFile(opts, project, release, file) {
return httpRequest({
uri: `${SENTRY_API_ROOT}/projects/${opts.organization}/${project}/releases/${release}/files/`,
uri: `${SENTRY_API_ROOT}/projects/${
opts.organization
}/${project}/releases/${release}/files/`,
method: 'POST',
auth: {
user: opts.key,
......@@ -74,15 +78,16 @@ function uploadReleaseFile(opts, project, release, file) {
file: fs.createReadStream(file.path),
name: path.basename(file.path),
},
}).then(function (result) {
}).then(function(result) {
if (result.response.statusCode === 201) {
return;
}
throw new Error(`Uploading file failed: ${result.response.statusCode}: ${result.body}`);
throw new Error(
`Uploading file failed: ${result.response.statusCode}: ${result.body}`
);
});
}
/**
* Upload a stream of Vinyl files as a Sentry release.
*
......@@ -99,25 +104,25 @@ function uploadReleaseFile(opts, project, release, file) {
*/
module.exports = function uploadToSentry(opts, projects, release) {
// Create releases in every project
const releases = projects.map(function (project) {
const releases = projects.map(function(project) {
gulpUtil.log(`Creating release '${release}' in project '${project}'`);
return createRelease(opts, project, release);
});
return through.obj(function (file, enc, callback) {
return through.obj(function(file, enc, callback) {
Promise.all(releases)
.then(function () {
.then(function() {
gulpUtil.log(`Uploading ${path.basename(file.path)}`);
const uploads = projects.map(function (project) {
const uploads = projects.map(function(project) {
return uploadReleaseFile(opts, project, release, file);
});
return Promise.all(uploads);
})
.then(function () {
.then(function() {
callback();
})
.catch(function (err) {
.catch(function(err) {
gulpUtil.log('Sentry upload failed: ', err);
callback(err);
});
......
......@@ -9,12 +9,7 @@
module.exports = {
bundles: {
jquery: ['jquery'],
angular: [
'angular',
'angular-route',
'ng-tags-input',
'angular-toastr',
],
angular: ['angular', 'angular-route', 'ng-tags-input', 'angular-toastr'],
katex: ['katex'],
showdown: ['showdown'],
raven: ['raven-js'],
......@@ -29,7 +24,5 @@ module.exports = {
// 2. The module is itself a compiled Browserify bundle containing
// internal require() statements, which should not be processed
// when including the bundle in another project.
noParseModules: [
'jquery',
],
noParseModules: ['jquery'],
};
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