Update build tooling

This commit is contained in:
Martin Kleinschrodt 2018-06-07 09:47:08 +02:00
parent d239943580
commit c379c23786
9 changed files with 16210 additions and 392 deletions

1
.gitignore vendored
View File

@ -14,3 +14,4 @@ app/src/core/*.js.map
tests.js
app/build
build

View File

@ -10,19 +10,11 @@
<style> html, body {
background: #59c6ff;
margin: 0;
overscroll-behavior-y: contain;
} </style>
<link rel="stylesheet" href="./assets/fonts/fonts.css">
<script>
// Register service worker if supported.
if ("serviceWorker" in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register("service-worker.js");
});
}
</script>
<script type="module" src="node_modules/@webcomponents/webcomponentsjs/webcomponents-bundle.js"></script>
<script type="module" src="./src/elements/app.js"></script>

View File

@ -3,7 +3,6 @@
"shell": "src/elements/app.js",
"sources": [
"src/**/*.js",
"vendor/**/*.js",
"manifest/**",
"package.json",
"manifest.json"
@ -15,11 +14,5 @@
"assets/**/*"
],
"moduleResolution": "node",
"npm": true,
"builds": [
{
"name": "web",
"preset": "es6-bundled"
}
]
"npm": true
}

View File

@ -2,8 +2,8 @@
"use strict";
var { buildCordova } = require("../../lib/build");
var path = require("path");
const { buildCordova } = require("../../lib/build");
const path = require("path");
module.exports = function(context) {
const platforms = context.opts.cordova.platforms;

View File

@ -3,11 +3,11 @@
const gulp = require("gulp");
const { argv } = require("yargs");
const { buildChrome, buildElectron, compile, buildDashboard } = require("./lib/build");
const http = require("http");
const st = require("st");
const { buildChrome, buildElectron, bundle, buildDashboard } = require("./lib/build");
const { updateLanguageFiles, buildTranslationsFile } = require("./lib/locale");
gulp.task("bundle", () => bundle(argv.dest, argv));
// Deploy a minified/built version of the app to a given destination folder
gulp.task("build", () => {
let promises = [];
@ -24,19 +24,6 @@ gulp.task("build", () => {
return Promise.all(promises);
});
gulp.task("compile", () => {
return compile(argv.watch);
});
gulp.task("serve", function() {
var port = argv.port || 8080;
console.log("Serving the app on a local server on port 8080. " +
"To view it, open your web browser and navigate to http://localhost:8080/app/");
http.createServer(
st({ path: "", cache: false, index: "index.html" })
).listen(port);
});
const supportedLanguages = ["en", "de", "es"];
gulp.task("update-langfiles", () => {
@ -44,13 +31,10 @@ gulp.task("update-langfiles", () => {
});
gulp.task("build-transfile", () => {
return buildTranslationsFile("resources/translations/",
"app/src/ui/locale/translations.js", supportedLanguages);
return buildTranslationsFile("resources/translations/", "app/src/ui/locale/translations.js", supportedLanguages);
});
gulp.task("build-dashboard", () => {
const { dest, watch } = argv;
return buildDashboard(dest, watch);
});
gulp.task("default", ["compile", "serve"]);

View File

@ -10,12 +10,11 @@ const insertLines = require("gulp-insert-lines");
const { exec } = require("child_process");
const builder = require("electron-builder");
const archiver = require("archiver");
const browserify = require("browserify");
const source = require("vinyl-source-stream");
const tsify = require("tsify");
const watchify = require("watchify");
const gutil = require("gulp-util");
const { getSignVendorPath } = require("electron-builder-lib/out/windowsCodeSign");
const { PolymerProject, getOptimizeStreams, addServiceWorker } = require("polymer-build");
const mergeStream = require("merge-stream");
const { Transform } = require("stream");
const { projectConfig, swConfig } = require("./config");
const projectRoot = path.resolve(__dirname, "..");
const appDir = path.join(projectRoot, "app");
@ -23,7 +22,7 @@ const appDir = path.join(projectRoot, "app");
function promisify(f) {
return function() {
return new Promise((resolve, reject) => {
f(...arguments, (err) => {
f(...arguments, err => {
if (err) {
reject(err);
} else {
@ -38,85 +37,8 @@ const copy = promisify(fs.copy);
const rmdir = promisify(rimraf);
const writeFile = promisify(fs.writeFile);
function compileCore(watch = false) {
let b = browserify(`${appDir}/src/core/main.ts`, {
standalone: "padlock",
cache: {},
packageCache: {},
debug: true
}).plugin(tsify);
function bundle() {
const stream = b.bundle();
stream.on("error", (e) => gutil.log(e.message));
return stream
.pipe(source("padlock.js"))
.pipe(gulp.dest(`${appDir}/src/`));
}
if (watch) {
b.plugin(watchify);
b.on("update", bundle);
}
return bundle();
}
function compileCoreLite(watch = false) {
let b = browserify(`${appDir}/src/core/main-lite.ts`, {
standalone: "padlock",
cache: {},
packageCache: {},
debug: true
}).plugin(tsify);
function bundle() {
const stream = b.bundle();
stream.on("error", (e) => gutil.log(e.message));
return stream
.pipe(source("padlock-lite.js"))
.pipe(gulp.dest(`${appDir}/src/`));
}
if (watch) {
b.plugin(watchify);
b.on("update", bundle);
}
return bundle();
}
function compileTests(watch = false) {
let b = browserify(`${projectRoot}/test/main.ts`, {
cache: {},
packageCache: {},
debug: true
}).plugin(tsify);
function bundle() {
return b.bundle()
.pipe(source("tests.js"))
.pipe(gulp.dest(`${projectRoot}/test/`));
}
if (watch) {
b.plugin(watchify);
b.on("update", bundle);
}
return bundle();
}
function compile(watch = false) {
compileCore(watch);
compileCoreLite(watch);
compileTests(watch);
}
function copyFiles(source, dest, files) {
return Promise.all(files.map((f) => copy(path.resolve(source, f), path.resolve(dest, f))));
return Promise.all(files.map(f => copy(path.resolve(source, f), path.resolve(dest, f))));
}
/**
@ -144,14 +66,86 @@ function pexec(command, logStderr = true, logStdout) {
});
}
function bundle(dest = "build") {
const bundler = `${projectRoot}/node_modules/polymer-bundler/lib/bin/polymer-bundler.js`;
const crisper = `${projectRoot}/node_modules/crisper/bin/crisper`;
const flags = `--inline-scripts --inline-css -r ${appDir}`;
let cmd = `node ${bundler} ${flags} index.html | node ${crisper} --html ${dest}/index.html --js ${dest}/build.js`;
return rmdir(dest)
.then(() => copyFiles(appDir, dest, ["assets"]))
.then(() => pexec(cmd));
function streamToPromise(stream) {
return new Promise(function(resolve, reject) {
stream.on("finish", resolve);
stream.on("error", reject);
});
}
class InjectSWLoader extends Transform {
constructor(path) {
super({ objectMode: true });
this.path = path;
}
_transform(file, _, done) {
if (file.path == path) {
let contents = file.contents.toString();
contents = contents.replace(
/<head>/i,
`<head>
<script>
// Register service worker if supported.
if ("serviceWorker" in navigator) {
window.addEventListener("load", function() {
navigator.serviceWorker.register("service-worker.js");
});
}
</script>
`
);
file.contents = new Buffer(contents);
}
done(null, file);
}
}
async function bundle(dest = "build", { bundle, amd, sw, compile }) {
dest = path.resolve(projectRoot, dest);
process.chdir(appDir);
const project = new PolymerProject(projectConfig);
const entryPoint = path.resolve(appDir, project.config.entrypoint);
let stream = mergeStream(project.sources(), project.dependencies());
if (sw) {
stream = stream.pipe(new InjectSWLoader(entryPoint));
}
if (bundle) {
stream = stream.pipe(project.bundler());
}
const optStreams = getOptimizeStreams({
js: {
moduleResolution: project.config.moduleResolution,
transformModulesToAmd: amd,
compile: compile
},
entrypointPath: project.config.entrypoint,
rootDir: project.config.root
});
for (const s of optStreams) {
stream = stream.pipe(s);
}
await rmdir(dest);
stream = stream.pipe(gulp.dest(dest));
await streamToPromise(stream);
if (sw) {
await addServiceWorker({
buildRoot: dest,
project: project,
swPrecacheConfig: swConfig,
bundled: false
});
}
}
function buildChrome() {
@ -168,107 +162,101 @@ function buildChrome() {
Object.assign(manifest, { version: v, description, author });
return writeFile(path.join(buildDir, "manifest.json"), JSON.stringify(manifest, null, " "));
})
.then(() => new Promise((resolve, reject) => {
let output = fs.createWriteStream(path.join(dest, `padlock-v${version}-chrome.zip`));
let archive = archiver.create("zip");
archive.directory(buildDir, "padlock");
archive.on("error", reject);
archive.on("finish", resolve);
archive.pipe(output);
archive.finalize();
}));
.then(
() =>
new Promise((resolve, reject) => {
let output = fs.createWriteStream(path.join(dest, `padlock-v${version}-chrome.zip`));
let archive = archiver.create("zip");
archive.directory(buildDir, "padlock");
archive.on("error", reject);
archive.on("finish", resolve);
archive.pipe(output);
archive.finalize();
})
);
}
function buildElectron(options) {
const buildDir = path.join("dist", "electron");
const { name, version, description, author, licence, homepage, dependencies, main } =
require(path.join(projectRoot, "package.json"));
async function buildElectron(options) {
const buildDir = path.join("app", "build", "electron");
const distDir = path.join("dist", "electron");
const { name, version, description, author, licence, homepage, dependencies, main } = require(path.join(
projectRoot,
"package.json"
));
const appMeta = {
name, version, description, author, licence, homepage, dependencies, main,
name,
version,
description,
author,
licence,
homepage,
dependencies,
main,
productName: "Padlock"
};
return rmdir(buildDir)
.then(() => copyFiles(".", "dist/electron", [ "main.js" ]))
.then(() => writeFile(path.join(buildDir, "package.json"), JSON.stringify(appMeta, null, " ")))
.then(() => pexec(`pushd ${buildDir} && npm install --production && popd`))
.then(() => bundle(path.join(buildDir, "app")))
.then(() => builder.build({
mac: options.mac && ["default"],
win: options.win && ["nsis"],
linux: options.linux && ["AppImage"],
config: {
appId: "com.maklesoft.padlock",
publish: {
provider: "github",
owner: "maklesoft",
repo: "padlock"
},
directories: {
buildResources: "resources/electron",
app: "dist/electron",
output: "dist"
},
win: {
publisherName: "Open Source Developer, Martin Kleinschrodt",
sign: (opts) => {
return getSignVendorPath().then((vendorPath) => {
const signTool = path.join(vendorPath, "windows-10", process.arch, "signtool.exe");
const cmd = `${signTool} sign /n "${opts.options.publisherName}" ` +
`/t http://time.certum.pl/ /fd sha1 /v "${opts.path}"`;
await pexec("cd ${appDir} && polymer build --name electron --js-compile --bundle --js-transform-modules-to-amd");
await rmdir(distDir);
await copy(buildDir, distDir);
await copy("./main.js", distDir);
await writeFile(path.join(distDir, "package.json"), JSON.stringify(appMeta, null, " "));
await pexec(`cd ${distDir} && npm install --production`);
await builder.build({
mac: options.mac && ["default"],
win: options.win && ["nsis"],
linux: options.linux && ["AppImage"],
config: {
appId: "com.maklesoft.padlock",
publish: {
provider: "github",
owner: "maklesoft",
repo: "padlock"
},
directories: {
buildResources: "resources/electron",
app: "dist/electron",
output: "dist"
},
win: {
publisherName: "Open Source Developer, Martin Kleinschrodt",
sign: opts => {
return getSignVendorPath().then(vendorPath => {
const signTool = path.join(vendorPath, "windows-10", process.arch, "signtool.exe");
const cmd =
`${signTool} sign /n "${opts.options.publisherName}" ` +
`/t http://time.certum.pl/ /fd sha1 /v "${opts.path}"`;
return pexec(cmd, true, true);
});
}
},
linux: {
category: "Utility"
},
protocols: {
name: "Padlock",
schemes: ["padlock"]
return pexec(cmd, true, true);
});
}
},
publish: options.release && "always"
}));
}
function streamToPromise(stream) {
return new Promise(function(resolve, reject) {
stream.on("finish", resolve);
stream.on("error", reject);
linux: {
category: "Utility"
},
protocols: {
name: "Padlock",
schemes: ["padlock"]
}
},
publish: options.release && "always"
});
}
function buildCordova(platform, dest) {
// iOS requires the source files to be bundled into a single file
// whereas doing that causes problems on Android, so we leave the files as-is.
const prepFiles = platform === "ios" ? bundle(dest) :
copyFiles(appDir, dest, ["bower_components", "assets", "src", "index.html"]);
async function buildCordova(platform, dest) {
await pexec("cd ${appDir} && polymer build --name cordova");
await copy(path.resolve(appDir, "build", "cordova", dest));
return prepFiles
.then(() => {
let lines = "\n<script src=\"cordova.js\"></script>\n";
const stream = gulp
.src("index.html", { cwd: dest })
.pipe(
insertLines({
after: /<head>/i,
lineAfter: '\n<script src="cordova.js"></script>\n'
})
)
.pipe(gulp.dest(dest));
if (platform === "ios") {
lines += `
<script>
window.customElements = window.customElements || {};
window.customElements.forcePolyfill = true;
window.ShadyDOM = {force: true};
</script>
`;
}
const stream = gulp.src("index.html", {cwd: dest})
.pipe(insertLines({
"after": /<head>/i,
"lineAfter": lines
}))
.pipe(gulp.dest(dest));
return streamToPromise(stream);
});
return streamToPromise(stream);
}
function buildDashboard(dest = "build/dashboard", watch = false) {
@ -277,7 +265,8 @@ function buildDashboard(dest = "build/dashboard", watch = false) {
const flags = `--inline-scripts --inline-css -r ${appDir}`;
const source = "src/cloud-dashboard/cloud-dashboard.html";
const targetDir = `${dest}/elements/cloud-dashboard`;
const bundleCmd = `node ${bundler} ${flags} ${source} | node ${crisper} --html ` +
const bundleCmd =
`node ${bundler} ${flags} ${source} | node ${crisper} --html ` +
`${targetDir}/cloud-dashboard.html --js ${targetDir}/cloud-dashboard.js`;
function doIt() {
@ -295,17 +284,15 @@ function buildDashboard(dest = "build/dashboard", watch = false) {
fs.watch("app", { recursive: true }, doIt);
}
return Promise.resolve()
// rmdir(dest)
.then(() => fs.ensureDir(targetDir))
.then(() => doIt());
return (
Promise.resolve()
// rmdir(dest)
.then(() => fs.ensureDir(targetDir))
.then(() => doIt())
);
}
module.exports = {
compileCore,
compileTests,
compile,
bundle,
buildChrome,
buildElectron,

View File

@ -7,13 +7,15 @@ const url = require("url");
const os = require("os");
const fs = require("fs-extra");
const uuid = require("uuid/v4");
const { debug, test } = require("yargs").argv;
const { debug, test, dir } = require("yargs").argv;
const ElectronStore = require("electron-store");
if (debug || test) {
app.setPath("userData", path.join(app.getPath("temp"), app.getName()));
}
const indexPath = path.resolve(__dirname, test ? "test" : dir || "app", "index.html");
const defaultDBPath = path.join(app.getPath("userData"), "data.pls");
const settings = (global.settings = new ElectronStore({
name: "settings",
@ -203,7 +205,7 @@ function createWindow() {
// and load the index.html of the app.
win.loadURL(
url.format({
pathname: path.resolve(__dirname, test ? "test/index.html" : "app/build/dev/index.html"),
pathname: indexPath,
protocol: "file:",
slashes: true
})

16178
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -34,9 +34,12 @@
"karma-chai": "^0.1.0",
"karma-chrome-launcher": "^2.1.1",
"karma-mocha": "^1.3.0",
"merge-stream": "^1.0.1",
"mocha": "^3.4.2",
"polymer-analyzer": "^2.2.0",
"polymer-build": "^3.0.1",
"polymer-bundler": "^2.3.1",
"polymer-cli": "^1.7.2",
"prettier": "^1.13.4",
"rimraf": "^2.5.4",
"st": "^1.2.0",
@ -57,7 +60,9 @@
"browser": "cd app && polymer serve --open",
"lint": "eslint app/**/*.js && prettier --list-different 'app/src/**/*.{js,ts}'",
"test": "karma start --single-run --browsers ChromeHeadless karma.conf.js",
"app": "electron .",
"app": "npm run build:electron && electron . --dir app/build/electron",
"build:electron": "cd app && polymer build --name electron --js-compile --bundle --js-transform-modules-to-amd && cd ..",
"build:pwa": "cd app && polymer build --name pwa --add-service-worker",
"build:mac": "gulp build --mac --silent",
"build:win": "gulp build --win --silent",
"build:linux": "gulp build --linux --silent",