Compare commits

...

15 commits

11 changed files with 191 additions and 164 deletions

View file

@ -31,7 +31,7 @@ steps:
# event: # event:
# - tag # - tag
# ref: # ref:
# - refs/tags/v[0-9].[0-9].[0-9] # - refs/tags/v[0-9]*.[0-9]*.[0-9]*
- name: prepare release - name: prepare release
image: manuth/silverstripe-dev image: manuth/silverstripe-dev
commands: commands:

18
.vscode/launch.json vendored
View file

@ -4,6 +4,24 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{
"args": [
"Watch"
],
"name": "Execute Watch Task",
"program": "${workspaceFolder}/node_modules/gulp/bin/gulp.js",
"request": "launch",
"skipFiles": [
"<node_internals>/**"
],
"type": "pwa-node",
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/gulpfile.ts",
"${workspaceFolder}/gulp/**/*.ts",
"!**/node_modules/**"
]
},
{ {
"type": "chrome", "type": "chrome",
"request": "launch", "request": "launch",

20
.vscode/tasks.json vendored
View file

@ -34,17 +34,17 @@
{ {
"base": "$gulp-tsc", "base": "$gulp-tsc",
"background": { "background": {
"activeOnStart": false, "activeOnStart": true,
"beginsPattern": "^(Starting compilation in watch mode|File change detected. Starting incremental compilation)", "beginsPattern": "(Starting compilation in watch mode|File change detected. Starting incremental compilation)",
"endsPattern": "^Found \\d+ errors. Watching for file changes." "endsPattern": "Found \\d+ errors. Watching for file changes."
} }
}, },
{ {
"base": "$node-sass", "base": "$node-sass",
"background": { "background": {
"activeOnStart": false, "activeOnStart": true,
"beginsPattern": "^Rebuilding scss-code.", "beginsPattern": "Building scss-code.",
"endsPattern": "^Rebuilding scss-code finished." "endsPattern": "Building scss-code finished."
} }
} }
], ],
@ -63,16 +63,16 @@
"base": "$gulp-tsc", "base": "$gulp-tsc",
"background": { "background": {
"activeOnStart": false, "activeOnStart": false,
"beginsPattern": "^(Starting compilation in watch mode|File change detected. Starting incremental compilation)", "beginsPattern": "(Starting compilation in watch mode|File change detected. Starting incremental compilation)",
"endsPattern": "^Found \\d+ errors. Watching for file changes." "endsPattern": "Found \\d+ errors. Watching for file changes."
} }
}, },
{ {
"base": "$node-sass", "base": "$node-sass",
"background": { "background": {
"activeOnStart": false, "activeOnStart": false,
"beginsPattern": "^Rebuilding scss-code.", "beginsPattern": "Building scss-code.",
"endsPattern": "^Rebuilding scss-code finished." "endsPattern": "Building scss-code finished."
} }
} }
], ],

View file

@ -1,4 +1,4 @@
import Path = require("upath"); import { dirname, join } from "upath";
/** /**
* Provides settings for building the project. * Provides settings for building the project.
@ -100,7 +100,7 @@ export class Settings
*/ */
public RootPath(...path: string[]): string public RootPath(...path: string[]): string
{ {
return Path.join(Path.dirname(__dirname), ...path); return join(dirname(__dirname), ...path);
} }
/** /**

View file

@ -1,19 +1,18 @@
import { Server, Socket } from "net"; import { Server, Socket } from "net";
import browserSync = require("browser-sync"); import browserSync = require("browser-sync");
import browserify = require("browserify"); import browserify = require("browserify");
import log = require("fancy-log"); import logger = require("fancy-log");
import FileSystem = require("fs-extra"); import { emptyDir, mkdirp, pathExists, remove } from "fs-extra";
import { TaskFunction } from "gulp"; import { dest, parallel, series, src, TaskFunction, watch } from "gulp";
import gulp = require("gulp");
import gulpIf = require("gulp-if"); import gulpIf = require("gulp-if");
import rename = require("gulp-rename"); import rename = require("gulp-rename");
import sass = require("gulp-sass"); import sass = require("gulp-sass");
import terser = require("gulp-terser"); import terser = require("gulp-terser");
import merge = require("merge-stream"); import merge = require("merge-stream");
import minimist = require("minimist"); import minimist = require("minimist");
import PromiseQueue = require("promise-queue");
import { parseArgsStringToArgv } from "string-argv"; import { parseArgsStringToArgv } from "string-argv";
import Path = require("upath"); import tsify = require("tsify");
import { changeExt, dirname, join, parse, relative } from "upath";
import buffer = require("vinyl-buffer"); import buffer = require("vinyl-buffer");
import vinylSourceStream = require("vinyl-source-stream"); import vinylSourceStream = require("vinyl-source-stream");
import Watchify = require("watchify"); import Watchify = require("watchify");
@ -104,16 +103,16 @@ export async function Clean(): Promise<void>
for (let directory of directories) for (let directory of directories)
{ {
await FileSystem.emptyDir(settings.RootPath(directory)); await emptyDir(settings.RootPath(directory));
} }
if (await FileSystem.pathExists(settings.TestThemePath())) if (await pathExists(settings.TestThemePath()))
{ {
await FileSystem.remove(settings.TestThemePath()); await remove(settings.TestThemePath());
} }
await FileSystem.mkdirp(settings.TestThemePath()); await mkdirp(settings.TestThemePath());
await FileSystem.remove(settings.TestThemePath()); await remove(settings.TestThemePath());
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
await require("create-symlink")(settings.RootPath(), settings.TestThemePath(), { type: "junction" }); await require("create-symlink")(settings.RootPath(), settings.TestThemePath(), { type: "junction" });
} }
@ -193,11 +192,12 @@ function BrowserSync(filePath?: string): TaskFunction
* Builds the project. * Builds the project.
*/ */
export async function Build(): Promise<void> export async function Build(): Promise<void>
{
return new Promise(
(resolve, reject) =>
{ {
if (settings.Watch) if (settings.Watch)
{ {
log.info(watchStartMessage);
syncer.init({ syncer.init({
open: false, open: false,
proxy: "http://localhost", proxy: "http://localhost",
@ -209,16 +209,23 @@ export async function Build(): Promise<void>
online: false online: false
}); });
gulp.watch(settings.ThemeSource("**"), { usePolling: true }, gulp.series(Theme, BrowserSync("*.css"))); watch(settings.ThemeSource("**"), { usePolling: true }, series(Theme, BrowserSync("*.css")));
gulp.watch(settings.TemplateSource("**"), { usePolling: true }, gulp.series(Templates, BrowserSync())); watch(settings.TemplateSource("**"), { usePolling: true }, series(Templates, BrowserSync()));
} }
await Promise.all( parallel(Library, Theme, Templates)(
[ (error) =>
Library(), {
Theme(), if (error)
Templates() {
]); reject(error);
}
else
{
resolve();
}
});
});
} }
/** /**
@ -227,10 +234,11 @@ export async function Build(): Promise<void>
* @returns * @returns
* The pipeline to execute. * The pipeline to execute.
*/ */
export async function Library(): Promise<NodeJS.ReadWriteStream> export function Library(): NodeJS.ReadWriteStream
{ {
let streams: Array<Promise<NodeJS.ReadWriteStream>> = []; let errorMessages: string[] = [];
let queue = new PromiseQueue(); let streams: NodeJS.ReadWriteStream[] = [];
let queue: NodeJS.ReadWriteStream[] = [];
let tsConfigFile = settings.TypeScriptProjectRoot("tsconfig.json"); let tsConfigFile = settings.TypeScriptProjectRoot("tsconfig.json");
// eslint-disable-next-line @typescript-eslint/no-var-requires // eslint-disable-next-line @typescript-eslint/no-var-requires
let tsConfig = require(tsConfigFile); let tsConfig = require(tsConfigFile);
@ -242,21 +250,28 @@ export async function Library(): Promise<NodeJS.ReadWriteStream>
debug: settings.Debug debug: settings.Debug
}; };
{
let errorMessages: string[] = [];
let files = (tsConfig.files as string[]).map( let files = (tsConfig.files as string[]).map(
(file) => Path.relative(settings.TypeScriptSourceRoot(), settings.TypeScriptProjectRoot(file))); (file) => relative(settings.TypeScriptSourceRoot(), settings.TypeScriptProjectRoot(file)));
if (settings.Watch)
{
logger.info(watchStartMessage);
}
for (let file of files) for (let file of files)
{ {
let builder = (): NodeJS.ReadWriteStream =>
{
let stream: NodeJS.ReadWriteStream;
let bundler = browserify( let bundler = browserify(
{ {
...optionBase, ...optionBase,
basedir: settings.LibraryPath(Path.dirname(file)), basedir: settings.LibraryPath(dirname(file)),
entries: [ entries: [
settings.TypeScriptSourceRoot(file) settings.TypeScriptSourceRoot(file)
], ],
standalone: Path.join(Path.dirname(file), Path.parse(file).name) standalone: join(dirname(file), parse(file).name)
}); });
if (settings.Watch) if (settings.Watch)
@ -265,18 +280,12 @@ export async function Library(): Promise<NodeJS.ReadWriteStream>
} }
bundler.plugin( bundler.plugin(
// eslint-disable-next-line @typescript-eslint/no-var-requires tsify,
require("tsify"),
{ {
project: tsConfigFile project: tsConfigFile
}); });
let bundle = async (): Promise<NodeJS.ReadWriteStream> => stream = bundler.bundle().on(
{
return new Promise<NodeJS.ReadWriteStream>(
(resolve) =>
{
let stream = bundler.bundle().on(
"error", "error",
(error) => (error) =>
{ {
@ -286,11 +295,11 @@ export async function Library(): Promise<NodeJS.ReadWriteStream>
{ {
let result = new RegExp(`^(${error["fileName"]})\\((\\d+|\\d+(,\\d+){1,3})\\): .* TS([\\d]+): (.*)$`).exec(message); let result = new RegExp(`^(${error["fileName"]})\\((\\d+|\\d+(,\\d+){1,3})\\): .* TS([\\d]+): (.*)$`).exec(message);
errorMessages.push(message); errorMessages.push(message);
console.log(`${Path.relative(process.cwd(), result[1])}(${result[2]}): ${result[4]} ${result[5]}`); console.log(`${relative(process.cwd(), result[1])}(${result[2]}): ${result[4]} ${result[5]}`);
} }
} }
).pipe( ).pipe(
vinylSourceStream(Path.changeExt(file, "js")) vinylSourceStream(changeExt(file, "js"))
).pipe( ).pipe(
buffer() buffer()
).pipe( ).pipe(
@ -299,56 +308,58 @@ export async function Library(): Promise<NodeJS.ReadWriteStream>
terser() terser()
) )
).pipe( ).pipe(
gulp.dest(settings.LibraryPath()) dest(settings.LibraryPath())
); ).on(
stream.on(
"end", "end",
() => () =>
{ {
if (settings.Watch && ((queue.getQueueLength() + queue.getPendingLength()) === 1)) if (settings.Watch)
{ {
if (queue.includes(stream))
{
queue.splice(queue.indexOf(stream), 1);
}
if (queue.length === 0)
{
logger.info(watchFinishMessage(errorMessages.length));
if (errorMessages.length === 0) if (errorMessages.length === 0)
{ {
syncer.reload("*.js"); syncer.reload("*.js");
} }
log.info(watchFinishMessage(errorMessages.length));
}
errorMessages.splice(0, errorMessages.length); errorMessages.splice(0, errorMessages.length);
resolve(stream); }
}
}); });
});
};
if (settings.Watch) if (settings.Watch)
{ {
bundler.on( bundler.once(
"update", "update",
() => () =>
{ {
if ((queue.getQueueLength() + queue.getPendingLength()) === 0) console.log(`Update called for ${file}: ${queue.length}`);
if (queue.length === 0)
{ {
log.info(incrementalMessage); logger.info(incrementalMessage);
} }
queue.add( queue.push(builder());
async () =>
{
return bundle();
});
}); });
} }
let build = (): Promise<NodeJS.ReadWriteStream> => queue.add(bundle); return stream;
build.displayName = Build.displayName; };
build.description = Build.description;
streams.push(build()); let stream = builder();
} queue.push(stream);
streams.push(stream);
} }
return merge(await Promise.all(streams)) as NodeJS.ReadWriteStream; return merge(streams);
} }
Library.description = "Builds the TypeScript- and JavaScript-library."; Library.description = "Builds the TypeScript- and JavaScript-library.";
@ -359,14 +370,14 @@ Library.description = "Builds the TypeScript- and JavaScript-library.";
* @returns * @returns
* The pipeline to execute. * The pipeline to execute.
*/ */
export async function Theme(): Promise<NodeJS.ReadWriteStream> export function Theme(): NodeJS.ReadWriteStream
{ {
if (settings.Watch) if (settings.Watch)
{ {
console.log("Rebuilding scss-code."); logger.info("Building scss-code.");
} }
return gulp.src( return src(
settings.ThemeSource("main.scss"), settings.ThemeSource("main.scss"),
{ {
sourcemaps: settings.Debug, sourcemaps: settings.Debug,
@ -400,7 +411,7 @@ export async function Theme(): Promise<NodeJS.ReadWriteStream>
parsedPath.basename = "mantra"; parsedPath.basename = "mantra";
}) })
).pipe( ).pipe(
gulp.dest( dest(
settings.StylePath(), settings.StylePath(),
settings.Debug ? settings.Debug ?
{ {
@ -413,7 +424,7 @@ export async function Theme(): Promise<NodeJS.ReadWriteStream>
{ {
if (settings.Watch) if (settings.Watch)
{ {
console.log("Rebuilding scss-code finished."); logger.info("Building scss-code finished.");
} }
}); });
} }
@ -428,9 +439,9 @@ Theme.description = "Builds the theme.";
*/ */
export function Templates(): NodeJS.ReadWriteStream export function Templates(): NodeJS.ReadWriteStream
{ {
return gulp.src( return src(
settings.TemplateSource("**")).pipe( settings.TemplateSource("**")).pipe(
gulp.dest(settings.TemplatePath())); dest(settings.TemplatePath()));
} }
Templates.description = "Builds the templates."; Templates.description = "Builds the templates.";
@ -461,7 +472,7 @@ export async function Stop(): Promise<void>
} }
catch catch
{ {
log.info("The specified task is not running."); logger.info("The specified task is not running.");
} }
} }

43
package-lock.json generated
View file

@ -74,6 +74,17 @@
} }
} }
}, },
"@es-joy/jsdoccomment": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.4.4.tgz",
"integrity": "sha512-ua4qDt9dQb4qt5OI38eCZcQZYE5Bq3P0GzgvDARdT8Lt0mAUpxKTPy8JGGqEvF77tG1irKDZ3WreeezEa3P43w==",
"dev": true,
"requires": {
"comment-parser": "^1.1.5",
"esquery": "^1.4.0",
"jsdoctypeparser": "^9.0.0"
}
},
"@eslint/eslintrc": { "@eslint/eslintrc": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.1.tgz", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.1.tgz",
@ -130,9 +141,9 @@
} }
}, },
"@manuth/eslint-plugin-typescript": { "@manuth/eslint-plugin-typescript": {
"version": "2.3.9", "version": "2.3.10",
"resolved": "https://registry.npmjs.org/@manuth/eslint-plugin-typescript/-/eslint-plugin-typescript-2.3.9.tgz", "resolved": "https://registry.npmjs.org/@manuth/eslint-plugin-typescript/-/eslint-plugin-typescript-2.3.10.tgz",
"integrity": "sha512-FkomtLTmkza9sSDOGAOhESrTxgVsQqQbgWs4aWubWGMRnRsjYrNVeaq5sHJMy9Tcovn6hccqSOjAwWxdmIYjkg==", "integrity": "sha512-xxYj3W9ZtuzEpFt21NtpPW4PHdFLEsh7GDjJ6oxI3SdAzC8cKk4T1erqCOOiYDeZrhwv4YiACxFGM6gs/yXrCw==",
"dev": true, "dev": true,
"requires": { "requires": {
"lodash.merge": "^4.6.2", "lodash.merge": "^4.6.2",
@ -782,12 +793,6 @@
"integrity": "sha1-akDsfr0kGO5p7jl+SOQhaSaKEL8=", "integrity": "sha1-akDsfr0kGO5p7jl+SOQhaSaKEL8=",
"dev": true "dev": true
}, },
"@types/promise-queue": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/@types/promise-queue/-/promise-queue-2.2.0.tgz",
"integrity": "sha512-9QLtid6GxEWqpF+QImxBRG6bSVOHtpAm2kXuIyEvZBbSOupLvqhhJv8uaHbS8kUL8FDjzH3RWcSyC/52WOVtGw==",
"dev": true
},
"@types/serve-static": { "@types/serve-static": {
"version": "1.13.9", "version": "1.13.9",
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.9.tgz",
@ -3713,13 +3718,15 @@
} }
}, },
"eslint-plugin-jsdoc": { "eslint-plugin-jsdoc": {
"version": "32.3.4", "version": "34.0.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-32.3.4.tgz", "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-34.0.1.tgz",
"integrity": "sha512-xSWfsYvffXnN0OkwLnB7MoDDDDjqcp46W7YlY1j7JyfAQBQ+WnGCfLov3gVNZjUGtK9Otj8mEhTZTqJu4QtIGA==", "integrity": "sha512-fXFyhM571aW1Eg5Z9INobK1VeMIcfflY4cnqDYFf5KRUwcR5R+TQIC6pU2C+4xA84s7lML3zi7UIbPOWBjU1YQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@es-joy/jsdoccomment": "^0.4.4",
"comment-parser": "1.1.5", "comment-parser": "1.1.5",
"debug": "^4.3.1", "debug": "^4.3.1",
"esquery": "^1.4.0",
"jsdoctypeparser": "^9.0.0", "jsdoctypeparser": "^9.0.0",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"regextras": "^0.7.1", "regextras": "^0.7.1",
@ -7208,12 +7215,6 @@
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true "dev": true
}, },
"promise-queue": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/promise-queue/-/promise-queue-2.2.5.tgz",
"integrity": "sha1-L29ffA9tCBCelnZZx5uIqe1ek7Q=",
"dev": true
},
"pseudomap": { "pseudomap": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
@ -9086,9 +9087,9 @@
} }
}, },
"tsify": { "tsify": {
"version": "5.0.3", "version": "5.0.4",
"resolved": "https://registry.npmjs.org/tsify/-/tsify-5.0.3.tgz", "resolved": "https://registry.npmjs.org/tsify/-/tsify-5.0.4.tgz",
"integrity": "sha512-Cg/3efnvTrU7L4gnAfXt8p6dp+WefE28qItDKQj4lN0X9Jw2KS2EWKh5/dizXwfVNbT0guAoUce8CqCDWjFDLg==", "integrity": "sha512-XAZtQ5OMPsJFclkZ9xMZWkSNyMhMxEPsz3D2zu79yoKorH9j/DT4xCloJeXk5+cDhosEibu4bseMVjyPOAyLJA==",
"dev": true, "dev": true,
"requires": { "requires": {
"convert-source-map": "^1.1.0", "convert-source-map": "^1.1.0",

View file

@ -19,7 +19,7 @@
}, },
"dependencies": {}, "dependencies": {},
"devDependencies": { "devDependencies": {
"@manuth/eslint-plugin-typescript": "^2.3.9", "@manuth/eslint-plugin-typescript": "^2.3.10",
"@manuth/tsconfig": "^1.2.9", "@manuth/tsconfig": "^1.2.9",
"@manuth/typescript-eslint-plugin": "^1.3.5", "@manuth/typescript-eslint-plugin": "^1.3.5",
"@types/bootstrap": "^5.0.13", "@types/bootstrap": "^5.0.13",
@ -35,22 +35,21 @@
"@types/jquery": "^3.5.5", "@types/jquery": "^3.5.5",
"@types/merge-stream": "^1.1.2", "@types/merge-stream": "^1.1.2",
"@types/minimist": "^1.2.1", "@types/minimist": "^1.2.1",
"@types/node": "^15.0.1", "@types/node": "^15.0.2",
"@types/promise-queue": "^2.2.0",
"@types/vinyl-buffer": "^1.0.0", "@types/vinyl-buffer": "^1.0.0",
"@types/vinyl-source-stream": "0.0.30", "@types/vinyl-source-stream": "0.0.30",
"@types/watchify": "^3.11.0", "@types/watchify": "^3.11.0",
"@typescript-eslint/eslint-plugin": "^4.22.0", "@typescript-eslint/eslint-plugin": "^4.23.0",
"@typescript-eslint/eslint-plugin-tslint": "^4.22.0", "@typescript-eslint/eslint-plugin-tslint": "^4.23.0",
"@typescript-eslint/parser": "^4.22.0", "@typescript-eslint/parser": "^4.23.0",
"bootstrap": "^5.0.0", "bootstrap": "^5.0.0",
"browser-sync": "^2.26.14", "browser-sync": "^2.26.14",
"browserify": "^17.0.0", "browserify": "^17.0.0",
"create-symlink": "^1.0.0", "create-symlink": "^1.0.0",
"eslint": "^7.25.0", "eslint": "^7.26.0",
"eslint-plugin-deprecation": "^1.2.0", "eslint-plugin-deprecation": "^1.2.1",
"eslint-plugin-import": "^2.22.1", "eslint-plugin-import": "^2.22.1",
"eslint-plugin-jsdoc": "^32.3.3", "eslint-plugin-jsdoc": "^34.0.1",
"fancy-log": "^1.3.3", "fancy-log": "^1.3.3",
"fs-extra": "^10.0.0", "fs-extra": "^10.0.0",
"gulp": "^4.0.2", "gulp": "^4.0.2",
@ -63,10 +62,9 @@
"minimist": "^1.2.5", "minimist": "^1.2.5",
"node-sass-tilde-importer": "^1.0.2", "node-sass-tilde-importer": "^1.0.2",
"popper.js": "^1.16.0", "popper.js": "^1.16.0",
"promise-queue": "^2.2.5",
"string-argv": "^0.3.1", "string-argv": "^0.3.1",
"ts-node": "^9.1.1", "ts-node": "^9.1.1",
"tsify": "^5.0.3", "tsify": "^5.0.4",
"tslint": "^6.1.3", "tslint": "^6.1.3",
"typescript": "^4.2.4", "typescript": "^4.2.4",
"typescript-tslint-plugin": "^1.0.1", "typescript-tslint-plugin": "^1.0.1",

View file

@ -1,4 +1,3 @@
<!doctype html> <!doctype html>
<html class="h-100"> <html class="h-100">
<head> <head>