Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial checkin of the monorepo structure + sw-appcache-behavior #1

Merged
merged 1 commit into from
May 16, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["es2015"]
}
17 changes: 17 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"extends": "google",
"rules": {
"max-nested-callbacks": 0,
"valid-jsdoc": [
2,
{
"requireParamDescription": false,
"requireReturnDescription": false,
"prefer": {
"return": "returns"
}
}
],
"no-warning-comments": 0
}
}
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
node_modules
build
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1 @@
# progress-helpers
Docs to come.
61 changes: 61 additions & 0 deletions build-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import childProcess from 'child_process';
import path from 'path';
import promisify from 'promisify-node';

export let globPromise = promisify('glob');

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that this is a style thing, and we have time constraints, I wouldn't block this but I'm opening a separate issue to discuss naming conventions. #2

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK.


/**
* Wrapper on top of childProcess.spawn() that returns a promise which rejects
* when the child process has a non-zero exit code and resolves otherwise.
*
* @param {String} command The command to spawn.
* @param {Array.<String>} args The parameters to pass to the command.
* @returns {Promise} Settles once command completes. Resolves if the exit code
* is 0, and rejects otherwise.
*/
export function processPromiseWrapper(command, args) {
return new Promise((resolve, reject) => {
const process = childProcess.spawn(command, args, {stdio: 'inherit'});
process.on('error', reject);
process.on('close', code => {
if (code === 0) {
resolve();
} else {
reject(`Error ${code} returned from ${command} ${args}`);
}
});
});
}

/**
* Promise.all() rejects immediately as soon as the first Promise rejects.
* This wrapper will run each Promise to resolution, and if one or more
* rejected, reject at the end with a concatenated error message.
*
* @param {Array.<Promise>} promises The promises to wait on.
* @returns {Promise.<*>} Resolves with null if all the promises resolve.
* Otherwise, rejects with a concatenated error.
*/
export function promiseAllWrapper(promises) {
let rejected = [];
return Promise.all(promises.map(promise => {
return promise.catch(error => rejected.push(error));
})).then(() => rejected.length ? Promise.reject(rejected.join('\n')) : null);
}

/**
* Helper function that runs a task against all projects, or just one of them.
* It will collect all the results and reject if any of the tasks rejects.
*
* @param {Function} task The function to run.
* @param {String} projectOrStar Either the name of a project, or '*' for all.
* @param {...Object} [args] Optional additional arguments to pass to task.
* @returns {Promise.<*>} Resolves with null if all the promises resolve.
* Otherwise, rejects with a concatenated error.
*/
export function taskHarness(task, projectOrStar, ...args) {
return globPromise(`projects/${projectOrStar}/package.json`)
.then(projects => promiseAllWrapper(
projects.map(project => task(path.dirname(project), ...args))
));
}
158 changes: 158 additions & 0 deletions gulpfile.babel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
/*
Copyright 2016 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import eslint from 'gulp-eslint';
import express from 'express';
import gulp from 'gulp';
import minimist from 'minimist';
import mocha from 'gulp-mocha';
import npmPath from 'npm-path';
import path from 'path';
import promisify from 'promisify-node';
import runSequence from 'run-sequence';
import serveIndex from 'serve-index';
import serveStatic from 'serve-static';
import {globPromise, processPromiseWrapper, promiseAllWrapper, taskHarness} from './build-utils.js';

const fsePromise = promisify('fs-extra');
const tmpPromise = promisify('tmp');
const ghPagesPromise = promisify('gh-pages');

const options = minimist(process.argv.slice(2));
const projectOrStar = options.project || '*';

// Before doing anything, modify process.env.PATH so the the ChromeDriver
// and documentation binaries in node_modules/.bin are picked up.
npmPath.setSync();

/**
* Lints a given project.
* @param project The path to a project directory.
* @returns {Promise} Resolves if linting succeeds, rejects if it fails.
*/
const lintPackage = project => {
return new Promise((resolve, reject) => {
gulp.src([`${project}/**/*.js`, `!${project}/build/**`])
.pipe(eslint())
.pipe(eslint.format())
.pipe(eslint.results(results => {
if ((results.warningCount + results.errorCount) > 0) {
reject(`Linting '${project}' failed.`);
} else {
resolve();
}
}));
});
};

/**
* Buids a given project.
* @param project The path to a project directory.
* @returns {Promise} Resolves if building succeeds, rejects if it fails.
*/
const buildPackage = project => {
const buildDir = `${project}/build`;

// Copy over package.json and README.md so that build/ contains what we
// need to publish to npm.
return fsePromise.emptyDir(buildDir)
.then(() => fsePromise.copy(`${project}/package.json`,
`${buildDir}/package.json`))
.then(() => fsePromise.copy(`${project}/README.md`,
`${buildDir}/README.md`))
.then(() => {
// Let each project define its own build process.
const build = require(`./${project}/build.js`);
return build();
});
};

/**
* Documents a given project.
* @param project The path to a project directory.
* @returns {Promise} Resolves if documenting succeeds, rejects if it fails.
*/
const documentPackage = (project, tmpDir) => {
// TODO: Reconsider the docuementation process.
return globPromise(`${project}/src/**/*.js`).then(files => {
const args = ['build', ...files, '--format', 'html', '--output',
path.join(tmpDir, project)];
return processPromiseWrapper('documentation', args);
});
};

/**
* Publishes a given project to npm.
* @param project The path to a project directory.
* @returns {Promise} Resolves if publishing succeeds, rejects if it fails.
*/
const publishPackage = project => {
return processPromiseWrapper('npm', ['publish', `${project}/build`]);
};

gulp.task('lint', () => {
return taskHarness(lintPackage, projectOrStar);
});

gulp.task('test', () => {
return gulp.src(`projects/${projectOrStar}/test/*.js`, {read: false})
.pipe(mocha());
});

gulp.task('build', () => {
return taskHarness(buildPackage, projectOrStar);
});

gulp.task('build:watch', unusedCallback => {
gulp.watch(`projects/${projectOrStar}/src/**/*`, ['build']);
});

gulp.task('serve', unusedCallback => {
const port = options.port || 3000;
const app = express();
const rootDirectory = projectOrStar === '*' ?
'projects' :
path.join('projects', projectOrStar);

app.use(serveStatic(rootDirectory));
app.use(serveIndex(rootDirectory, {view: 'details'}));
app.listen(port, () => {
console.log(`Serving '${rootDirectory}' at http://localhost:${port}/`);
});
});

gulp.task('documentation', () => {
if (projectOrStar !== '*') {
throw Error('Please do not use the --project= parameter when generating ' +
'documentation.');
}

return tmpPromise.dir().then(tmpDir => {
return taskHarness(documentPackage, projectOrStar, tmpDir)
.then(() => ghPagesPromise.publish(tmpDir));
});
});

gulp.task('publish', ['lint', 'build'], () => {
if (projectOrStar === '*') {
throw Error('Please use the --project= parameter to specify a project.');
}

return taskHarness(publishPackage, projectOrStar);
});

gulp.task('default', callback => {
runSequence(['lint', 'test'], 'documentation', 'build', callback);
});
9 changes: 9 additions & 0 deletions npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

45 changes: 45 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
{
"name": "sw-helpers",
"version": "0.0.0",
"description": "Top-level scripts and dependencies for the sw-helpers monorepo. Not meant to be published to npm.",
"private": true,
"main": "gulpfile.babel.js",
"repository": {
"type": "git",
"url": "git+https://github.com/GoogleChrome/sw-helpers.git"
},
"author": "Google's Web DevRel Team",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/GoogleChrome/sw-helpers/issues"
},
"homepage": "https://github.com/GoogleChrome/sw-helpers#readme",
"devDependencies": {
"babel": "^6.5.2",
"babel-preset-es2015": "^6.6.0",
"blueimp-md5": "^2.3.0",
"browserify": "^13.0.1",
"chai": "^3.5.0",
"chromedriver": "^2.21.2",
"documentation": "^4.0.0-beta2",
"eslint": "^2.8.0",
"eslint-config-google": "^0.5.0",
"express": "^4.13.4",
"fs-extra": "^0.30.0",
"gh-pages": "^0.11.0",
"glob": "^7.0.3",
"gulp": "^3.9.1",
"gulp-eslint": "^2.0.0",
"gulp-mocha": "^2.2.0",
"idb": "^1.1.0",
"minimist": "^1.2.0",
"npm-path": "^1.1.0",
"parse-appcache-manifest": "^0.2.0",
"promisify-node": "^0.4.0",
"run-sequence": "^1.1.5",
"serve-index": "^1.7.3",
"serve-static": "^1.10.2",
"sw-testing-helpers": "0.0.14",
"tmp": "0.0.28"
}
}
1 change: 1 addition & 0 deletions projects/sw-appcache-behavior/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Docs to come.
38 changes: 38 additions & 0 deletions projects/sw-appcache-behavior/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
Copyright 2016 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// It's unclear whether a bespoke build process is needed for each sub-project,
// or whether the top-level gulp task can just use a common, browserify-based
// build with each sub-project exporting a list of files to use as entry points.

import browserify from 'browserify';
import fs from 'fs';
import path from 'path';

module.exports = () => {
return Promise.all([
'client-runtime.js',
'appcache-behavior-import.js'
].map(file => {
return new Promise((resolve, reject) => {
const bundler = browserify(path.join(__dirname, 'src', file));

bundler.bundle()
.pipe(fs.createWriteStream(path.join(__dirname, 'build', file)))
.on('error', reject)
.on('finish', resolve);
});
}));
};
10 changes: 10 additions & 0 deletions projects/sw-appcache-behavior/demo/another-index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<html manifest="another-manifest.appcache">
<head>
<title>Possibly Cached</title>
<link rel="stylesheet" href="common.css">
</head>
<body>
<p>I might be served from the cache.</p>
<script src="../build/register-service-worker.js" data-service-worker="service-worker.js"></script>
</body>
</html>
12 changes: 12 additions & 0 deletions projects/sw-appcache-behavior/demo/another-manifest.appcache
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
CACHE MANIFEST
# v1

CACHE:
common.css
../build/register-service-worker.js

NETWORK:
no-cache/

FALLBACK:
can-fallback.html fallback.html
10 changes: 10 additions & 0 deletions projects/sw-appcache-behavior/demo/another-page.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<html manifest="manifest.appcache">
<head>
<title>Possibly Cached</title>
<link rel="stylesheet" href="common.css">
</head>
<body>
<p>I might be served from the cache.</p>
<script src="../build/client-runtime.js" data-service-worker="service-worker.js"></script>
</body>
</html>
9 changes: 9 additions & 0 deletions projects/sw-appcache-behavior/demo/can-fallback.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<html>
<head>
<title>Can Fallback</title>
<link rel="stylesheet" href="common.css">
</head>
<body>
<p>I'm only displayed when the network is available.</p>
</body>
</html>
Empty file.