@journeyapps/reactor-lib-server 2.1.4 → 2.1.5
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.
- package/dist/@types/ReactorConfig.d.ts +9 -0
- package/dist/@types/ReactorModule.d.ts +34 -0
- package/dist/@types/basic-html.d.ts +21 -0
- package/dist/@types/index.d.ts +5 -0
- package/dist/@types/logging.d.ts +2 -0
- package/dist/@types/reactor-html.d.ts +8 -0
- package/dist/ReactorConfig.js +3 -0
- package/dist/ReactorModule.js +77 -0
- package/dist/basic-html.js +77 -0
- package/dist/index.js +8 -0
- package/dist/logging.js +9 -0
- package/dist/reactor-html.js +40 -0
- package/media/core-fragment.html +7 -0
- package/media/index.html +97 -0
- package/package.json +7 -2
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ReactorModuleConfig } from './ReactorConfig';
|
|
2
|
+
export interface ReactorModuleOptions {
|
|
3
|
+
directory: string;
|
|
4
|
+
resolveGlobalEnv: (key: string) => string | undefined;
|
|
5
|
+
}
|
|
6
|
+
export declare class ReactorModule {
|
|
7
|
+
options: ReactorModuleOptions;
|
|
8
|
+
protected confFile: string;
|
|
9
|
+
protected conf?: ReactorModuleConfig;
|
|
10
|
+
protected confPackage?: {
|
|
11
|
+
name: string;
|
|
12
|
+
version: string;
|
|
13
|
+
};
|
|
14
|
+
protected fragment: string | null;
|
|
15
|
+
constructor(options: ReactorModuleOptions);
|
|
16
|
+
getEnvs(): Record<string, string>;
|
|
17
|
+
get name(): string;
|
|
18
|
+
get packageJson(): {
|
|
19
|
+
name: string;
|
|
20
|
+
version: string;
|
|
21
|
+
};
|
|
22
|
+
get config(): ReactorModuleConfig;
|
|
23
|
+
get loaderPayload(): {
|
|
24
|
+
fragmentData: string;
|
|
25
|
+
background: string;
|
|
26
|
+
} | null;
|
|
27
|
+
}
|
|
28
|
+
export declare const loadModules: (options: {
|
|
29
|
+
env: {
|
|
30
|
+
MODULES: string[];
|
|
31
|
+
} & {
|
|
32
|
+
[key: string]: any;
|
|
33
|
+
};
|
|
34
|
+
}) => ReactorModule[];
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { Request, Response } from 'express';
|
|
2
|
+
import { CheerioAPI } from 'cheerio';
|
|
3
|
+
export interface CreateHtmlGeneratorOptions {
|
|
4
|
+
domTransform?: (data: CheerioAPI) => void;
|
|
5
|
+
indexFile: string;
|
|
6
|
+
templateVars?: {};
|
|
7
|
+
title: string;
|
|
8
|
+
}
|
|
9
|
+
export declare const createHtmlGenerator: (options: CreateHtmlGeneratorOptions) => Promise<(env?: Record<string, string>) => string>;
|
|
10
|
+
export interface CreateBaseIndexMiddlewareOptions extends CreateHtmlGeneratorOptions {
|
|
11
|
+
transform?: (req: Request, res: Response, content: string) => Promise<string>;
|
|
12
|
+
getEnv?: (req: Request) => Record<string, string | undefined>;
|
|
13
|
+
}
|
|
14
|
+
export declare const createBaseIndexMiddleware: (options: CreateBaseIndexMiddlewareOptions) => Promise<(req: Request, res: Response) => Promise<void>>;
|
|
15
|
+
/**
|
|
16
|
+
* Escape environment variables to protect against XSS.
|
|
17
|
+
*
|
|
18
|
+
* The resulting value is both valid JSON and valid JS, and is safe to inject directly in a <script> tag.
|
|
19
|
+
*/
|
|
20
|
+
export declare function escapeEnv(values: Record<string, string | undefined>): string;
|
|
21
|
+
export declare function escapeForHtmlScript(value: any): string;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Application } from 'express';
|
|
2
|
+
import { ReactorModule } from './ReactorModule';
|
|
3
|
+
import { CheerioAPI } from 'cheerio';
|
|
4
|
+
export declare const serveModules: (options: {
|
|
5
|
+
app: Application;
|
|
6
|
+
modules: ReactorModule[];
|
|
7
|
+
}) => void;
|
|
8
|
+
export declare const createModuleLoaderContentTransformer: ($: CheerioAPI, modules: ReactorModule[]) => void;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadModules = exports.ReactorModule = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
6
|
+
const path = tslib_1.__importStar(require("path"));
|
|
7
|
+
const logging_1 = require("./logging");
|
|
8
|
+
const common_logger_1 = require("@journeyapps/common-logger");
|
|
9
|
+
class ReactorModule {
|
|
10
|
+
constructor(options) {
|
|
11
|
+
this.options = options;
|
|
12
|
+
this.confFile = path.join(options.directory, 'reactor.config.json');
|
|
13
|
+
this.confPackage = require(path.join(options.directory, 'package.json'));
|
|
14
|
+
this.fragment = null;
|
|
15
|
+
if (fs.existsSync(this.confFile)) {
|
|
16
|
+
this.conf = require(this.confFile);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
throw new Error(`No config file for ${options.directory} at ${this.confFile}`);
|
|
20
|
+
}
|
|
21
|
+
this.getEnvs();
|
|
22
|
+
}
|
|
23
|
+
getEnvs() {
|
|
24
|
+
return this.config.env.reduce((env, key) => {
|
|
25
|
+
const value = this.options.resolveGlobalEnv(key);
|
|
26
|
+
if (value == null) {
|
|
27
|
+
throw new Error(`Environment variable '${key}' required by Reactor module '${this.name}' is missing`);
|
|
28
|
+
}
|
|
29
|
+
env[key] = value;
|
|
30
|
+
return env;
|
|
31
|
+
}, {});
|
|
32
|
+
}
|
|
33
|
+
get name() {
|
|
34
|
+
return this.config.name;
|
|
35
|
+
}
|
|
36
|
+
get packageJson() {
|
|
37
|
+
return this.confPackage;
|
|
38
|
+
}
|
|
39
|
+
get config() {
|
|
40
|
+
var _a;
|
|
41
|
+
return Object.assign(Object.assign({}, this.conf), { env: (_a = this.conf.env) !== null && _a !== void 0 ? _a : [] });
|
|
42
|
+
}
|
|
43
|
+
get loaderPayload() {
|
|
44
|
+
if (this.config.loader) {
|
|
45
|
+
if (!this.fragment) {
|
|
46
|
+
this.fragment = fs.readFileSync(path.join(path.dirname(this.confFile), this.config.loader.fragment), {
|
|
47
|
+
encoding: 'utf-8'
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
background: this.config.loader.backgroundColor,
|
|
52
|
+
fragmentData: this.fragment
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.ReactorModule = ReactorModule;
|
|
59
|
+
const loadModules = (options) => {
|
|
60
|
+
return options.env.MODULES.map((m) => {
|
|
61
|
+
let directory = path.join(process.cwd(), m);
|
|
62
|
+
if (m.startsWith('@')) {
|
|
63
|
+
// this should be the directory that contains the reactor config file
|
|
64
|
+
directory = path.resolve(path.dirname(require.resolve(m, { paths: [process.cwd()] })), '..');
|
|
65
|
+
}
|
|
66
|
+
const module = new ReactorModule({
|
|
67
|
+
resolveGlobalEnv: (key) => {
|
|
68
|
+
return options.env[key];
|
|
69
|
+
},
|
|
70
|
+
directory: directory
|
|
71
|
+
});
|
|
72
|
+
logging_1.reactorServerLogger.info(common_logger_1.Log.green('Loaded module'), common_logger_1.Log.bold(common_logger_1.Log.cyan(module.name)), common_logger_1.Log.purple(`${module.packageJson.name}@${module.packageJson.version}`), common_logger_1.Log.gray(`slug: ${module.config.slug}`), common_logger_1.Log.dim(directory));
|
|
73
|
+
logging_1.reactorServerLogger.debug(common_logger_1.Log.dim('Module environment'), common_logger_1.Log.bold(module.name), module.config.env);
|
|
74
|
+
return module;
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
exports.loadModules = loadModules;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createBaseIndexMiddleware = exports.createHtmlGenerator = void 0;
|
|
4
|
+
exports.escapeEnv = escapeEnv;
|
|
5
|
+
exports.escapeForHtmlScript = escapeForHtmlScript;
|
|
6
|
+
const tslib_1 = require("tslib");
|
|
7
|
+
const _ = tslib_1.__importStar(require("lodash"));
|
|
8
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
9
|
+
const path = tslib_1.__importStar(require("path"));
|
|
10
|
+
const cheerio_1 = require("cheerio");
|
|
11
|
+
_.templateSettings.interpolate = /<%=([\s\S]+?)%>/g;
|
|
12
|
+
const readFileCached = _.memoize((fileName) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
13
|
+
return yield fs.promises.readFile(fileName, 'utf8');
|
|
14
|
+
}));
|
|
15
|
+
const createHtmlGenerator = (options) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
16
|
+
var _a;
|
|
17
|
+
let index_data = yield readFileCached(options.indexFile);
|
|
18
|
+
// append the core stuff the to head which deals with ENV variables
|
|
19
|
+
let index_fragment = yield readFileCached(path.join(__dirname, '../media/core-fragment.html'));
|
|
20
|
+
const $ = (0, cheerio_1.load)(index_data);
|
|
21
|
+
$('head').prepend(`<title>${options.title}</title>`);
|
|
22
|
+
$('head').prepend(index_fragment);
|
|
23
|
+
// optionally mutate even more
|
|
24
|
+
(_a = options.domTransform) === null || _a === void 0 ? void 0 : _a.call(options, $);
|
|
25
|
+
// now turn it into a template so we can do variable substitution
|
|
26
|
+
const template = _.template($.html());
|
|
27
|
+
return (env = {}) => {
|
|
28
|
+
return template(Object.assign({ ENV: escapeEnv(Object.assign(Object.assign({}, env), { NODE_ENV: process.env.NODE_ENV || 'development' })) }, (options.templateVars || {})));
|
|
29
|
+
};
|
|
30
|
+
});
|
|
31
|
+
exports.createHtmlGenerator = createHtmlGenerator;
|
|
32
|
+
const createBaseIndexMiddleware = (options) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
33
|
+
const generator = yield (0, exports.createHtmlGenerator)(options);
|
|
34
|
+
return (req, res) => tslib_1.__awaiter(void 0, void 0, void 0, function* () {
|
|
35
|
+
var _a, _b;
|
|
36
|
+
try {
|
|
37
|
+
let env = ((_a = options.getEnv) === null || _a === void 0 ? void 0 : _a.call(options, req)) || {};
|
|
38
|
+
let index_data = generator(env);
|
|
39
|
+
if (options.transform) {
|
|
40
|
+
index_data = yield ((_b = options.transform) === null || _b === void 0 ? void 0 : _b.call(options, req, res, index_data));
|
|
41
|
+
}
|
|
42
|
+
res.send(index_data);
|
|
43
|
+
res.end();
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
res.status(500).end();
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
exports.createBaseIndexMiddleware = createBaseIndexMiddleware;
|
|
51
|
+
/**
|
|
52
|
+
* Escape environment variables to protect against XSS.
|
|
53
|
+
*
|
|
54
|
+
* The resulting value is both valid JSON and valid JS, and is safe to inject directly in a <script> tag.
|
|
55
|
+
*/
|
|
56
|
+
function escapeEnv(values) {
|
|
57
|
+
for (let value of Object.values(values)) {
|
|
58
|
+
if (value != null && typeof value != 'string') {
|
|
59
|
+
// This is not expected to happen. We just do this as an extra safety check, to avoid injecting nested values.
|
|
60
|
+
throw new Error(`Invalid environment variable: ${value}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return escapeForHtmlScript(values);
|
|
64
|
+
}
|
|
65
|
+
function escapeForHtmlScript(value) {
|
|
66
|
+
/*
|
|
67
|
+
Some background: https://dzone.com/articles/preventing-xss-vulnerabilities-when-developing-rub
|
|
68
|
+
https://portswigger.net/web-security/cross-site-scripting/preventing
|
|
69
|
+
Values injected in a <script> tag needs to be escaped for:
|
|
70
|
+
1. JavaScript: JSON.stringify covers this.
|
|
71
|
+
2. HTML: </script> in the middle of a JSON string still breaks out of the script. Using unicode escape sequences
|
|
72
|
+
covers this. At minimum, '<' and '>' should be escaped. Being extra cautious, we escape most characters except for a small allow-list.
|
|
73
|
+
*/
|
|
74
|
+
const jsSafe = JSON.stringify(value);
|
|
75
|
+
// Our allow-list includes valid JSON control characters, allowing this to work on objects in addition to strings.
|
|
76
|
+
return jsSafe.replace(/[^\w. ",:{}\\\[\]]/gi, (c) => '\\u' + ('0000' + c.charCodeAt(0).toString(16)).slice(-4));
|
|
77
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
tslib_1.__exportStar(require("./ReactorModule"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./ReactorConfig"), exports);
|
|
6
|
+
tslib_1.__exportStar(require("./basic-html"), exports);
|
|
7
|
+
tslib_1.__exportStar(require("./reactor-html"), exports);
|
|
8
|
+
tslib_1.__exportStar(require("./logging"), exports);
|
package/dist/logging.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reactorServerLogger = void 0;
|
|
4
|
+
const common_logger_1 = require("@journeyapps/common-logger");
|
|
5
|
+
exports.reactorServerLogger = new common_logger_1.Logger({
|
|
6
|
+
name: 'Reactor server',
|
|
7
|
+
level: common_logger_1.LogLevel.INFO,
|
|
8
|
+
transport: new common_logger_1.NodeConsoleLoggerTransport()
|
|
9
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createModuleLoaderContentTransformer = exports.serveModules = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const express_1 = tslib_1.__importDefault(require("express"));
|
|
6
|
+
const path = tslib_1.__importStar(require("path"));
|
|
7
|
+
const serveModules = (options) => {
|
|
8
|
+
options.modules.forEach((m) => {
|
|
9
|
+
options.app.use(`/module/${m.config.slug}`, express_1.default.static(path.join(m.options.directory, 'dist-module')));
|
|
10
|
+
});
|
|
11
|
+
};
|
|
12
|
+
exports.serveModules = serveModules;
|
|
13
|
+
const createModuleLoaderContentTransformer = ($, modules) => {
|
|
14
|
+
let module_names_window = modules.map((m) => m.packageJson.name);
|
|
15
|
+
modules.forEach((m) => $('head').append(`<script nonce="SCRIPT_NONCE"
|
|
16
|
+
defer
|
|
17
|
+
data-module="${m.packageJson.name}"
|
|
18
|
+
src="/module/${m.config.slug}/bundle.js"
|
|
19
|
+
type="text/javascript" />`));
|
|
20
|
+
$('head').append(`
|
|
21
|
+
<script nonce="SCRIPT_NONCE">
|
|
22
|
+
window.addEventListener('DOMContentLoaded', async (event) => {
|
|
23
|
+
// get all the reactor module classes
|
|
24
|
+
const module_classes = ${JSON.stringify(module_names_window)}.map(m => {
|
|
25
|
+
if(!window[m]){
|
|
26
|
+
throw new Error('Reactor module "' + m + '" was not loaded correctly.');
|
|
27
|
+
}
|
|
28
|
+
return window[m].default;
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
// call preboot on all of them if they have that method (such as the ReactorModule which inits the kernel)
|
|
32
|
+
module_classes.forEach(module => {
|
|
33
|
+
if (module.preboot) {
|
|
34
|
+
module.preboot(module_classes);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
</script>`);
|
|
39
|
+
};
|
|
40
|
+
exports.createModuleLoaderContentTransformer = createModuleLoaderContentTransformer;
|
package/media/index.html
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover" />
|
|
6
|
+
<meta name="theme-color" content="<%= LOADER_BACKGROUND_COLOR %>" />
|
|
7
|
+
<meta name="mobile-web-app-capable" content="yes" />
|
|
8
|
+
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
9
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
10
|
+
<style>
|
|
11
|
+
html,
|
|
12
|
+
body {
|
|
13
|
+
background: <%= LOADER_BACKGROUND_COLOR %>;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@keyframes fadein {
|
|
17
|
+
0%{
|
|
18
|
+
opacity: 0;
|
|
19
|
+
}
|
|
20
|
+
100%{
|
|
21
|
+
opacity: 1;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.loader {
|
|
26
|
+
opacity: 0;
|
|
27
|
+
top: 50%;
|
|
28
|
+
left: 50%;
|
|
29
|
+
transform: translateX(-50%) translateY(-50%);
|
|
30
|
+
position: absolute;
|
|
31
|
+
display: flex;
|
|
32
|
+
flex-direction: column;
|
|
33
|
+
align-items: center;
|
|
34
|
+
animation: fadein;
|
|
35
|
+
animation-duration: 0.3s;
|
|
36
|
+
animation-delay: 0.5s;
|
|
37
|
+
animation-direction: normal;
|
|
38
|
+
animation-fill-mode: forwards;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.loading-bar-wrapper {
|
|
42
|
+
width: 200px;
|
|
43
|
+
height: 4px;
|
|
44
|
+
background: rgba(255, 255, 255, 0.1);
|
|
45
|
+
margin-top: 40px;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.loading-text {
|
|
49
|
+
margin-top: 6px;
|
|
50
|
+
font-size: 11px;
|
|
51
|
+
color: white;
|
|
52
|
+
opacity: 0.2;
|
|
53
|
+
font-family: sans-serif;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.loading-bar {
|
|
57
|
+
width: 0%;
|
|
58
|
+
transition: width 0.3s;
|
|
59
|
+
height: 100%;
|
|
60
|
+
background: rgba(255, 255, 255, 0.3);
|
|
61
|
+
will-change: width;
|
|
62
|
+
}
|
|
63
|
+
</style>
|
|
64
|
+
<script nonce="SCRIPT_NONCE">
|
|
65
|
+
let loaded = new Set();
|
|
66
|
+
window.reactorModuleLoaded = (module) => {
|
|
67
|
+
const scripts = Array.from(document.scripts).filter((s) => !!s.dataset['module']);
|
|
68
|
+
// could be multiple modules with the same name due to workers etc...
|
|
69
|
+
if(loaded.has(module)){
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
loaded.add(module)
|
|
73
|
+
const loaderBar = document.querySelector('.loading-bar');
|
|
74
|
+
const loaderText = document.querySelector('.loading-text');
|
|
75
|
+
if (!loaderBar) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const percent = parseInt((loaded.size / scripts.length) * 100);
|
|
79
|
+
loaderBar.style.width = `${percent}%`;
|
|
80
|
+
if (percent === 100) {
|
|
81
|
+
loaderText.innerHTML = `Booting!`;
|
|
82
|
+
} else {
|
|
83
|
+
loaderText.innerHTML = `Loaded: ${module}`;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
</script>
|
|
87
|
+
</head>
|
|
88
|
+
<body>
|
|
89
|
+
<div class="loader">
|
|
90
|
+
<div class="loading-bar-wrapper">
|
|
91
|
+
<div class="loading-bar"></div>
|
|
92
|
+
</div>
|
|
93
|
+
<div class="loading-text">Loading...</div>
|
|
94
|
+
</div>
|
|
95
|
+
<div id="application"></div>
|
|
96
|
+
</body>
|
|
97
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@journeyapps/reactor-lib-server",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.5",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/",
|
|
6
6
|
"access": "public"
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"typings": "./dist/@types/index",
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@journeyapps/common-logger": "1.1.2",
|
|
18
|
-
"@journeyapps/reactor-lib-utils": "2.0.
|
|
18
|
+
"@journeyapps/reactor-lib-utils": "2.0.17",
|
|
19
19
|
"cheerio": "^1.2.0",
|
|
20
20
|
"express": "^5.2.1",
|
|
21
21
|
"lodash": "^4.18.1",
|
|
@@ -26,6 +26,11 @@
|
|
|
26
26
|
"@types/express-session": "^1.19.0",
|
|
27
27
|
"@types/lodash": "^4.17.25"
|
|
28
28
|
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"!dist/tsconfig.tsbuildinfo",
|
|
32
|
+
"media"
|
|
33
|
+
],
|
|
29
34
|
"scripts": {
|
|
30
35
|
"build": "../node_modules/.bin/tsc --build"
|
|
31
36
|
}
|