@nzz/q-cli 1.5.4 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/.vscode/settings.json +5 -0
  2. package/README.md +16 -1
  3. package/bin/commands/bootstrap.js +6 -0
  4. package/dev-server/routes/rendering-info.js +0 -1
  5. package/dev-server/routes/tool-default.js +0 -2
  6. package/dev-server/server.js +3 -0
  7. package/package.json +1 -1
  8. package/skeletons/custom-code-skeleton/.nvmrc +1 -1
  9. package/skeletons/custom-code-skeleton/.vscode/settings.json +5 -0
  10. package/skeletons/custom-code-skeleton/README.md +17 -6
  11. package/skeletons/custom-code-skeleton/index.d.ts +3 -0
  12. package/skeletons/custom-code-skeleton/package-lock.json +5563 -791
  13. package/skeletons/custom-code-skeleton/package.json +22 -12
  14. package/skeletons/custom-code-skeleton/q.config.json +2 -2
  15. package/skeletons/custom-code-skeleton/rollup.config.js +116 -70
  16. package/skeletons/custom-code-skeleton/src/App.scss +5 -0
  17. package/skeletons/custom-code-skeleton/src/App.svelte +3 -7
  18. package/skeletons/custom-code-skeleton/src/interfaces.ts +0 -0
  19. package/skeletons/custom-code-skeleton/src/{main-prod.js → main-prod.ts} +1 -0
  20. package/skeletons/custom-code-skeleton/src/main.scss +1 -0
  21. package/skeletons/custom-code-skeleton/src/main.ts +18 -0
  22. package/skeletons/custom-code-skeleton/tsconfig.json +15 -0
  23. package/skeletons/server-skeleton/auth/routes.js +0 -9
  24. package/skeletons/server-skeleton/index.js +1 -3
  25. package/skeletons/tool-skeleton/.nvmrc +1 -1
  26. package/skeletons/tool-skeleton/.travis.yml +26 -0
  27. package/skeletons/tool-skeleton/.vscode/settings.json +5 -0
  28. package/skeletons/tool-skeleton/Dockerfile +1 -1
  29. package/skeletons/tool-skeleton/LICENSE +20 -0
  30. package/skeletons/tool-skeleton/README.md +1 -1
  31. package/skeletons/tool-skeleton/index.js +1 -6
  32. package/skeletons/tool-skeleton/package-lock.json +8540 -2741
  33. package/skeletons/tool-skeleton/package.json +24 -12
  34. package/skeletons/tool-skeleton/rollup.config.js +75 -0
  35. package/skeletons/tool-skeleton/routes/fixtures/data.js +1 -3
  36. package/skeletons/tool-skeleton/routes/rendering-info/web.js +43 -20
  37. package/skeletons/tool-skeleton/routes/routes.js +1 -1
  38. package/skeletons/tool-skeleton/routes/script.js +4 -5
  39. package/skeletons/tool-skeleton/routes/stylesheet.js +4 -5
  40. package/skeletons/tool-skeleton/sass.config.js +66 -0
  41. package/skeletons/tool-skeleton/scripts_src/default.js +3 -0
  42. package/skeletons/tool-skeleton/styles_src/_variables.scss +1 -0
  43. package/skeletons/tool-skeleton/styles_src/main.scss +2 -0
  44. package/skeletons/tool-skeleton/test/e2e-tests.js +4 -6
  45. package/skeletons/tool-skeleton/views/dynamic/YourTool.scss +5 -0
  46. package/skeletons/tool-skeleton/views/dynamic/YourTool.svelte +19 -0
  47. package/skeletons/tool-skeleton/views/static/App.scss +5 -0
  48. package/skeletons/tool-skeleton/views/static/App.svelte +21 -0
  49. package/skeletons/tool-skeleton/views/static/components/Footer.svelte +31 -0
  50. package/skeletons/tool-skeleton/views/static/components/Header.svelte +7 -0
  51. package/skeletons/custom-code-skeleton/src/main.js +0 -10
  52. package/skeletons/tool-skeleton/styles_src/default.scss +0 -4
  53. package/skeletons/tool-skeleton/tasks/build.js +0 -98
@@ -4,7 +4,10 @@
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "build": "rm -rf styles && mkdir styles && node tasks/build.js",
7
+ "build": "rollup -c",
8
+ "autobuild": "rollup -c -w",
9
+ "dev": "run-p autobuild start",
10
+ "start": "nodemon index.js",
8
11
  "test": "lab -a @hapi/code -c -P tests --verbose --leaks"
9
12
  },
10
13
  "author": "",
@@ -14,20 +17,29 @@
14
17
  "url": "https://github.com/github/tool-skeleton.git"
15
18
  },
16
19
  "dependencies": {
17
- "@hapi/boom": "^9.1.1",
18
- "@hapi/hapi": "^20.1.3",
19
- "@hapi/inert": "^6.0.3",
20
- "ajv": "^6.12.6",
21
- "joi": "^17.4.0"
20
+ "@hapi/boom": "^9.1.4",
21
+ "@hapi/hapi": "^20.2.0",
22
+ "@hapi/inert": "^6.0.4",
23
+ "ajv": "^8.6.3",
24
+ "joi": "^17.4.2",
25
+ "svelte": "^3.44.2"
22
26
  },
23
27
  "devDependencies": {
24
28
  "@hapi/code": "^8.0.3",
25
- "@hapi/lab": "^22.0.5",
26
- "autoprefixer": "^10.2.5",
27
- "cssnano": "^4.1.11",
29
+ "@hapi/lab": "^24.3.2",
30
+ "@rollup/plugin-commonjs": "^21.0.0",
31
+ "@rollup/plugin-node-resolve": "^13.0.5",
32
+ "autoprefixer": "^10.4.0",
33
+ "cssnano": "^5.0.10",
28
34
  "glob": "^7.1.7",
29
- "postcss": "^8.3.0",
30
- "postcss-import": "^14.0.2",
31
- "sass": "^1.34.0"
35
+ "nodemon": "^2.0.13",
36
+ "npm-run-all": "^4.1.5",
37
+ "postcss": "^8.3.11",
38
+ "rollup": "^2.60.0",
39
+ "rollup-plugin-livereload": "^2.0.5",
40
+ "rollup-plugin-scss": "^3.0.0",
41
+ "rollup-plugin-svelte": "^7.1.0",
42
+ "rollup-plugin-terser": "^7.0.2",
43
+ "sass": "^1.43.4"
32
44
  }
33
45
  }
@@ -0,0 +1,75 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import * as crypto from "crypto";
4
+ import nodeResolve from "@rollup/plugin-node-resolve";
5
+ import commonjs from "@rollup/plugin-commonjs";
6
+ import svelte from "rollup-plugin-svelte";
7
+ import { terser } from "rollup-plugin-terser";
8
+ import livereload from "rollup-plugin-livereload";
9
+ import scss from "rollup-plugin-scss";
10
+ const sassConfig = require("./sass.config");
11
+
12
+ const production = !process.env.ROLLUP_WATCH;
13
+ const scriptsDir = path.join(__dirname, "/scripts_src/");
14
+ const filename = "default";
15
+ const scriptDirDefaultFileName = path.join(scriptsDir, `/${filename}.js`);
16
+
17
+ function writeHashmap(hashmapPath, file, fileext) {
18
+ const hash = crypto.createHash("md5");
19
+ hash.update(file.content, { encoding: "utf8" });
20
+ file.hash = hash.digest("hex");
21
+
22
+ const hashMap = {};
23
+ hashMap[file.name] = `${file.name}.${file.hash.substring(0, 8)}.${fileext}`;
24
+ fs.writeFileSync(hashmapPath, JSON.stringify(hashMap));
25
+ }
26
+
27
+ function generateHashmap() {
28
+ return {
29
+ name: "generateHashmap",
30
+ async generateBundle(outputOptions, bundle, isWrite) {
31
+ const scriptsDir = "scripts";
32
+ // Create directory if not yet exist or recreate directory if it already exists
33
+ if (!fs.existsSync(scriptsDir)) {
34
+ fs.mkdirSync(scriptsDir);
35
+ } else {
36
+ fs.rmdirSync(scriptsDir, { recursive: true });
37
+ fs.mkdirSync(scriptsDir);
38
+ }
39
+ writeHashmap(
40
+ "scripts/hashMap.json",
41
+ {
42
+ name: filename,
43
+ content: bundle[`${filename}.js`].code,
44
+ },
45
+ "js"
46
+ );
47
+ },
48
+ };
49
+ }
50
+
51
+ export default {
52
+ input: scriptDirDefaultFileName,
53
+ output: {
54
+ format: "iife",
55
+ // TODO: Rename 'window._q_your_tool.YourTool' to 'window._q_<tool-name>.<ToolName>'
56
+ name: "window._q_your_tool.YourTool",
57
+ file: `scripts/${filename}.js`,
58
+ },
59
+ plugins: [
60
+ svelte(),
61
+ scss({ ...sassConfig.get(production, writeHashmap) }),
62
+ nodeResolve({ browser: true }),
63
+ commonjs(),
64
+ !production && livereload({ watch: ["scripts"], delay: 800 }),
65
+ production && terser(),
66
+ generateHashmap(),
67
+ ],
68
+ watch: {
69
+ clearScreen: false,
70
+ },
71
+ onwarn: function (warning, warn) {
72
+ if (warning.code === "CIRCULAR_DEPENDENCY") return;
73
+ warn(warning);
74
+ },
75
+ };
@@ -1,7 +1,6 @@
1
1
  const fixtureDataDirectory = "../../resources/fixtures/data";
2
2
 
3
3
  // provide every fixture data file present in ../../resources/fixtures/data
4
- // has to be in sync with files created in build task - see ../../tasks/build.js
5
4
  const fixtureData = [require(`${fixtureDataDirectory}/basic.json`)];
6
5
 
7
6
  module.exports = {
@@ -9,9 +8,8 @@ module.exports = {
9
8
  method: "GET",
10
9
  options: {
11
10
  tags: ["api"],
12
- cors: true
13
11
  },
14
12
  handler: (request, h) => {
15
13
  return fixtureData;
16
- }
14
+ },
17
15
  };
@@ -1,21 +1,32 @@
1
1
  const Boom = require("@hapi/boom");
2
2
  const fs = require("fs");
3
3
  const path = require("path");
4
+ const Ajv = require("ajv");
4
5
 
6
+ const staticViewsDir = path.join(__dirname, "/../../views/static/");
5
7
  const stylesDir = path.join(__dirname, "/../../styles/");
8
+ const scriptsDir = path.join(__dirname, "../../scripts/");
9
+
10
+ require("svelte/register");
11
+ const staticTemplate = require(path.join(
12
+ staticViewsDir,
13
+ "/App.svelte"
14
+ )).default;
15
+ const styles = fs.readFileSync(path.join(stylesDir, "/default.css")).toString();
6
16
  const styleHashMap = require(path.join(stylesDir, "hashMap.json"));
17
+ const scriptHashMap = require(path.join(scriptsDir, "hashMap.json"));
7
18
 
8
19
  // POSTed item will be validated against given schema
9
20
  // hence we fetch the JSON schema...
10
21
  const schemaString = JSON.parse(
11
22
  fs.readFileSync(path.join(__dirname, "../../resources/", "schema.json"), {
12
- encoding: "utf-8"
23
+ encoding: "utf-8",
13
24
  })
14
25
  );
15
- const Ajv = require("ajv");
16
- const ajv = new Ajv();
17
26
 
27
+ const ajv = new Ajv({ strict: false });
18
28
  const validate = ajv.compile(schemaString);
29
+
19
30
  function validateAgainstSchema(item, options) {
20
31
  if (validate(item)) {
21
32
  return item;
@@ -43,32 +54,44 @@ module.exports = {
43
54
  options: {
44
55
  validate: {
45
56
  options: {
46
- allowUnknown: true
57
+ allowUnknown: true,
47
58
  },
48
- payload: validatePayload
49
- }
59
+ payload: validatePayload,
60
+ },
50
61
  },
51
- handler: async function(request, h) {
52
- const item = request.payload.item;
62
+ handler: async function (request, h) {
63
+ const toolRuntimeConfig = request.payload.toolRuntimeConfig;
64
+ const context = {
65
+ // TODO: Rename 'q_your_tool_' to 'q_<tool_name>_'
66
+ id: `q_your_tool_${toolRuntimeConfig.requestId}`,
67
+ displayOptions: toolRuntimeConfig.displayOptions || {},
68
+ item: request.payload.item,
69
+ };
70
+
71
+ const staticTemplateRender = staticTemplate.render(context);
53
72
 
54
73
  const renderingInfo = {
55
74
  polyfills: ["Promise"],
56
- stylesheets: [
57
- {
58
- name: styleHashMap["default"]
59
- }
60
- ],
75
+ stylesheets: [{ content: styles }, { name: styleHashMap["default"] }],
61
76
  scripts: [
77
+ { name: scriptHashMap["default"] },
78
+ // TODO: Rename 'new window._q_your_tool.YourTool' to 'new window._q_<tool-name>.<ToolName>'
62
79
  {
63
- content:
64
- 'var p = new Promise(function(resolve) { resolve(); }) p.then(function() { console.log ("tool-skeleton script executed")});'
65
- }
80
+ content: `
81
+ (function () {
82
+ var target = document.querySelector('#${context.id}_container');
83
+ target.innerHTML = "";
84
+ var props = ${JSON.stringify(context)};
85
+ new window._q_your_tool.YourTool({
86
+ "target": target,
87
+ "props": props
88
+ })
89
+ })();`,
90
+ },
66
91
  ],
67
- markup: `<h1>${item.title}</h1><h2>${
68
- item.subtitle
69
- }</h2><p>rendered by tool-skeleton`
92
+ markup: staticTemplateRender.html,
70
93
  };
71
94
 
72
95
  return renderingInfo;
73
- }
96
+ },
74
97
  };
@@ -4,5 +4,5 @@ module.exports = [
4
4
  require("./script.js"),
5
5
  require("./health.js"),
6
6
  require("./fixtures/data.js"),
7
- require("./locales.js")
7
+ require("./locales.js"),
8
8
  ].concat(require("./schema.js"));
@@ -4,15 +4,14 @@ module.exports = {
4
4
  method: "GET",
5
5
  path: "/script/{filename}.{hash}.{extension}",
6
6
  options: {
7
- cors: true,
8
7
  files: {
9
- relativeTo: path.join(__dirname, "/../scripts/")
10
- }
8
+ relativeTo: path.join(__dirname, "/../scripts/"),
9
+ },
11
10
  },
12
- handler: function(request, h) {
11
+ handler: function (request, h) {
13
12
  return h
14
13
  .file(`${request.params.filename}.${request.params.extension}`)
15
14
  .type("text/javascript")
16
15
  .header("cache-control", `max-age=${60 * 60 * 24 * 365}, immutable`); // 1 year
17
- }
16
+ },
18
17
  };
@@ -4,15 +4,14 @@ module.exports = {
4
4
  method: "GET",
5
5
  path: "/stylesheet/{filename}.{hash}.{extension}",
6
6
  options: {
7
- cors: true,
8
7
  files: {
9
- relativeTo: path.join(__dirname, "/../styles/")
10
- }
8
+ relativeTo: path.join(__dirname, "/../styles/"),
9
+ },
11
10
  },
12
- handler: function(request, h) {
11
+ handler: function (request, h) {
13
12
  return h
14
13
  .file(`${request.params.filename}.${request.params.extension}`)
15
14
  .type("text/css")
16
15
  .header("cache-control", `max-age=${60 * 60 * 24 * 365}, immutable`); // 1 year
17
- }
16
+ },
18
17
  };
@@ -0,0 +1,66 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const postcss = require("postcss");
4
+ const autoprefixer = require("autoprefixer");
5
+ const cssnano = require("cssnano");
6
+
7
+ function createOutputCssFunction(writeHashmapFunction) {
8
+ const outputCssFunction = (styles, styleNodes) => {
9
+ const stylesDir = "styles";
10
+
11
+ if (!fs.existsSync(stylesDir)) {
12
+ fs.mkdirSync(stylesDir);
13
+ }
14
+
15
+ fs.writeFileSync(`styles/default.css`, styles);
16
+ writeHashmapFunction(
17
+ "styles/hashMap.json",
18
+ {
19
+ name: "default",
20
+ content: styles,
21
+ },
22
+ "css"
23
+ );
24
+ };
25
+
26
+ return outputCssFunction;
27
+ }
28
+
29
+ function getPostcssPlugins(isProduction) {
30
+ const postcssPlugins = [autoprefixer];
31
+
32
+ if (isProduction) {
33
+ postcssPlugins.push(cssnano);
34
+ }
35
+
36
+ return postcssPlugins;
37
+ }
38
+
39
+ function get(isProduction, writeHashmapFunction) {
40
+ const config = {
41
+ outputStyle: isProduction ? "compressed" : "expanded",
42
+ // Sourcemap generation (specifically writing the file to system) is currently not supported by rollup-plugin-sass (but soon!)
43
+ // See: https://github.com/thgh/rollup-plugin-scss/issues/7
44
+ // outFile: path.join(__dirname, "/styles/default.css"), // <- Uncomment after: https://github.com/thgh/rollup-plugin-scss/issues/7
45
+ sourceMap: !isProduction,
46
+ sourceMapEmbed: !isProduction, // Remove after: https://github.com/thgh/rollup-plugin-scss/issues/7
47
+ failOnError: !isProduction,
48
+ watch: [
49
+ path.join(__dirname, "/styles_src"),
50
+ path.join(__dirname, "/views"),
51
+ ],
52
+ processor: (css) =>
53
+ postcss(getPostcssPlugins(isProduction))
54
+ .process(css, {
55
+ from: path.join(__dirname, "/styles/default.css"),
56
+ to: path.join(__dirname, "/styles/default.css"),
57
+ map: isProduction ? false : { inline: true }, // Set to false after: https://github.com/thgh/rollup-plugin-scss/issues/7
58
+ })
59
+ .then((result) => result.css),
60
+ output: createOutputCssFunction(writeHashmapFunction),
61
+ };
62
+
63
+ return config;
64
+ }
65
+
66
+ module.exports = { get };
@@ -0,0 +1,3 @@
1
+ import YourTool from "./../views/dynamic/YourTool.svelte";
2
+ import "./../styles_src/main.scss";
3
+ export default YourTool;
@@ -0,0 +1 @@
1
+ $color: salmon;
@@ -0,0 +1,2 @@
1
+ @use "../views/static/App.scss";
2
+ @use "../views/dynamic/YourTool.scss";
@@ -20,9 +20,7 @@ before(async () => {
20
20
  try {
21
21
  server = Hapi.server({
22
22
  port: process.env.PORT || 3000,
23
- routes: {
24
- cors: true,
25
- },
23
+ routes: {},
26
24
  });
27
25
  server.validator(Joi);
28
26
  await server.register(require("@hapi/inert"));
@@ -103,7 +101,7 @@ lab.experiment("stylesheets endpoint", () => {
103
101
  });
104
102
 
105
103
  // all the fixtures render
106
- lab.experiment("all fixtures render", async () => {
104
+ lab.experiment("all fixtures render", () => {
107
105
  const fixtureFiles = glob.sync(
108
106
  `${__dirname}/../resources/fixtures/data/*.json`
109
107
  );
@@ -143,7 +141,7 @@ lab.experiment("rendering-info", () => {
143
141
  });
144
142
 
145
143
  lab.experiment("assets", () => {
146
- it("returnes stylesheet", async () => {
144
+ it("returns stylesheet", async () => {
147
145
  const fixture = fs.readFileSync(
148
146
  `${__dirname}/../resources/fixtures/data/basic.json`,
149
147
  { encoding: "utf-8" }
@@ -157,7 +155,7 @@ lab.experiment("assets", () => {
157
155
  },
158
156
  });
159
157
  const stylesheetRes = await server.inject(
160
- `/stylesheet/${res.result.stylesheets[0].name}`
158
+ `/stylesheet/${res.result.stylesheets[1].name}`
161
159
  );
162
160
  expect(stylesheetRes.statusCode).to.be.equal(200);
163
161
  });
@@ -0,0 +1,5 @@
1
+ @use "variables";
2
+
3
+ .your-tool__is-working-label {
4
+ color: variables.$color !important; // Please refrain from using '!important' :)
5
+ }
@@ -0,0 +1,19 @@
1
+ <script>
2
+ // TODO: Rename 'YourTool.svelte' to '<Tool-Name>.svelte' & change all references
3
+ let isWorkingLabel = "";
4
+ let i = 0;
5
+
6
+ function worksOnClick() {
7
+ i++;
8
+ isWorkingLabel = i < 5 ? "is working!" : "is still working!";
9
+ }
10
+ </script>
11
+
12
+ <!-- Client side code is written/imported inside this component (e.g. Client interaction components) -->
13
+ <div class="s-font-title">
14
+ Client-Side code
15
+ <span class="your-tool__is-working-label">{isWorkingLabel}</span>
16
+ </div>
17
+ <button class="s-button s-button--small" on:click={worksOnClick}>
18
+ Click me!
19
+ </button>
@@ -0,0 +1,5 @@
1
+ @use "variables";
2
+
3
+ h3 {
4
+ color: variables.$color !important; // Please refrain from using '!important' :)
5
+ }
@@ -0,0 +1,21 @@
1
+ <script>
2
+ import Header from "./components/Header.svelte";
3
+ import Footer from "./components/Footer.svelte";
4
+
5
+ export let item;
6
+ export let id;
7
+ export let displayOptions;
8
+ </script>
9
+
10
+ <!-- Static code is written/imported inside 'q-item-container' (e.g. Header, Title components) -->
11
+ <div class="q-item-container">
12
+ {#if !displayOptions.hideTitle}
13
+ <Header title={item.title} />
14
+ {#if item.subtitle}
15
+ <div class="s-q-item__subtitle s-font-note">{item.subtitle}</div>
16
+ {/if}
17
+ {/if}
18
+ <!-- TODO: Rename 'q-your-tool-container' to 'q-<tool-name>-container' & change all references -->
19
+ <div id="{id}_container" class="q-your-tool-container" />
20
+ <Footer notes={item.notes} sources={item.sources} acronym={item.acronym} />
21
+ </div>
@@ -0,0 +1,31 @@
1
+ <script>
2
+ export let notes;
3
+ export let sources;
4
+ export let acronym;
5
+ </script>
6
+
7
+ <div class="s-q-item__footer">
8
+ {#if notes}
9
+ <div class="s-q-item__footer__notes">{notes}</div>
10
+ {/if}
11
+ <div class="s-q-item__footer__details">
12
+ {#if sources && sources.length > 0}
13
+ <div class="s-q-item__footer__sources">
14
+ {#if sources.length > 1}Quellen:{:else}Quelle:{/if}
15
+ {#each sources as source, index}
16
+ {#if source.text !== ""}
17
+ {#if source.link && source.link.url && source.link.isValid}<a
18
+ href={source.link.url}
19
+ target="blank"
20
+ rel="noopener noreferrer">{source.text}</a
21
+ >{:else}{source.text}{/if}{#if index !== sources.length - 1 && sources[index + 1] !== ""},&nbsp;{/if}{/if}
22
+ {/each}
23
+ </div>
24
+ {/if}
25
+ {#if acronym}
26
+ <div class="s-q-item__footer__acronym">NZZ / {acronym}</div>
27
+ {:else}
28
+ <div class="s-q-item__footer__acronym">NZZ</div>
29
+ {/if}
30
+ </div>
31
+ </div>
@@ -0,0 +1,7 @@
1
+ <script>
2
+ export let title;
3
+ </script>
4
+
5
+ <h3 class="s-q-item__title">
6
+ {title}
7
+ </h3>
@@ -1,10 +0,0 @@
1
- import App from "./App.svelte";
2
-
3
- const app = new App({
4
- target: document.querySelector("#container"),
5
- props: {
6
- name: "custom-code-skeleton",
7
- },
8
- });
9
-
10
- export default app;
@@ -1,4 +0,0 @@
1
- $color: purple;
2
- h1 {
3
- color: $color;
4
- }
@@ -1,98 +0,0 @@
1
- const fs = require("fs");
2
- const path = require("path");
3
- const crypto = require("crypto");
4
-
5
- const sass = require("sass");
6
- const postcss = require("postcss");
7
- const postcssImport = require("postcss-import");
8
- const autoprefixer = require("autoprefixer");
9
- const cssnano = require("cssnano");
10
-
11
- const stylesDir = path.join(__dirname, "/../styles_src/");
12
-
13
- function writeHashmap(hashmapPath, files, fileext) {
14
- const hashMap = {};
15
- files
16
- .map(file => {
17
- const hash = crypto.createHash("md5");
18
- hash.update(file.content, { encoding: "utf8" });
19
- file.hash = hash.digest("hex");
20
- return file;
21
- })
22
- .map(file => {
23
- hashMap[file.name] = `${file.name}.${file.hash.substring(
24
- 0,
25
- 8
26
- )}.${fileext}`;
27
- });
28
-
29
- fs.writeFileSync(hashmapPath, JSON.stringify(hashMap));
30
- }
31
-
32
- async function compileStylesheet(name) {
33
- return new Promise((resolve, reject) => {
34
- const filePath = path.join(stylesDir, `${name}.scss`);
35
- fs.access(filePath, fs.constants.R_OK, err => {
36
- if (err) {
37
- reject(new Error(`stylesheet ${filePath} cannot be read`));
38
- process.exit(1);
39
- }
40
- sass.render(
41
- {
42
- file: filePath,
43
- outputStyle: "compressed"
44
- },
45
- (err, sassResult) => {
46
- if (err) {
47
- reject(err);
48
- } else {
49
- postcss()
50
- .use(postcssImport)
51
- .use(autoprefixer)
52
- .use(cssnano)
53
- .process(sassResult.css, {
54
- from: path.join(stylesDir, `${name}.css`)
55
- })
56
- .then(prefixedResult => {
57
- if (prefixedResult.warnings().length > 0) {
58
- console.log(`failed to compile stylesheet ${name}`);
59
- process.exit(1);
60
- }
61
- resolve(prefixedResult.css);
62
- });
63
- }
64
- }
65
- );
66
- });
67
- });
68
- }
69
-
70
- async function buildStyles() {
71
- try {
72
- // compile styles
73
- const styleFiles = [
74
- {
75
- name: "default",
76
- content: await compileStylesheet("default")
77
- }
78
- ];
79
-
80
- styleFiles.map(file => {
81
- fs.writeFileSync(`styles/${file.name}.css`, file.content);
82
- });
83
-
84
- writeHashmap("styles/hashMap.json", styleFiles, "css");
85
- } catch (err) {
86
- console.error(err);
87
- process.exit(1);
88
- }
89
- }
90
-
91
- Promise.all([buildStyles()])
92
- .then(res => {
93
- console.log("build complete");
94
- })
95
- .catch(err => {
96
- console.error(err.message);
97
- process.exit(1);
98
- });