1 | import * as fs from 'fs-extra';
|
2 | import {
|
3 | Clients,
|
4 | CompiledFiles,
|
5 | JavaScript,
|
6 | TypeScript,
|
7 | Transformer,
|
8 | CustomHTTPHeaders,
|
9 | HTTPHeaders,
|
10 | FileContentsResult
|
11 | } from '../index.d.ts';
|
12 | import * as chokidar from 'chokidar';
|
13 | import * as WebSocket from 'ws';
|
14 | import * as tsc from 'typescript';
|
15 | import * as babel from '@babel/core';
|
16 | import { resolveBareSpecifiers } from './babel-plugins/babel-plugin-transform-resolve-bare-specifiers.js';
|
17 | import { resolveImportPathExtensions } from './babel-plugins/babel-plugin-transform-resolve-import-path-extensions.js';
|
18 |
|
19 | export async function getFileContents(params: {
|
20 | url: string;
|
21 | compiledFiles: CompiledFiles;
|
22 | disableSpa: boolean;
|
23 | watchFiles: boolean;
|
24 | clients: Clients;
|
25 | transformer: Transformer | 'NOT_SET';
|
26 | }): Promise<Readonly<FileContentsResult>> {
|
27 |
|
28 | const cachedFileContents: Readonly<Buffer> | null | undefined = await returnFileContentsFromCache({
|
29 | url: params.url,
|
30 | compiledFiles: params.compiledFiles
|
31 | });
|
32 |
|
33 | if (
|
34 | cachedFileContents !== null &&
|
35 | cachedFileContents !== undefined
|
36 | ) {
|
37 | return {
|
38 | fileContents: cachedFileContents
|
39 | };
|
40 | }
|
41 | else {
|
42 |
|
43 | if (await (fs.exists as any)(params.url)) {
|
44 | const fileContents: Readonly<Buffer> = await fs.readFile(params.url);
|
45 |
|
46 | const transformedFileContents: Readonly<Buffer> = params.transformer === 'NOT_SET' ? fileContents : Buffer.from(params.transformer(fileContents.toString()));
|
47 |
|
48 | params.compiledFiles[params.url] = transformedFileContents;
|
49 |
|
50 | watchFile({
|
51 | filePath: params.url,
|
52 | watchFiles: params.watchFiles,
|
53 | clients: params.clients,
|
54 | compiledFiles: params.compiledFiles
|
55 | });
|
56 |
|
57 | return {
|
58 | fileContents: transformedFileContents
|
59 | };
|
60 | }
|
61 |
|
62 | if (!params.disableSpa) {
|
63 | const indexFileContents: Readonly<Buffer> = await fs.readFile(`./index.html`);
|
64 |
|
65 | return {
|
66 | fileContents: indexFileContents
|
67 | };
|
68 | }
|
69 | else {
|
70 | return 'FILE_NOT_FOUND';
|
71 | }
|
72 |
|
73 | }
|
74 | }
|
75 |
|
76 | async function returnFileContentsFromCache(params: {
|
77 | url: string;
|
78 | compiledFiles: CompiledFiles;
|
79 | }): Promise<Readonly<Buffer> | null | undefined> {
|
80 |
|
81 | const cachedFileContents: Readonly<Buffer> | null | undefined = params.compiledFiles[params.url];
|
82 |
|
83 | return cachedFileContents;
|
84 | }
|
85 |
|
86 | export function watchFile(params: {
|
87 | filePath: string;
|
88 | watchFiles: boolean;
|
89 | clients: Clients;
|
90 | compiledFiles: CompiledFiles;
|
91 | }) {
|
92 | if (params.watchFiles) {
|
93 | chokidar.watch(params.filePath).on('change', () => {
|
94 |
|
95 | params.compiledFiles[params.filePath] = null;
|
96 |
|
97 | Object.values(params.clients).forEach((client: Readonly<WebSocket>) => {
|
98 | try {
|
99 | client.send('RELOAD_MESSAGE');
|
100 | }
|
101 | catch(error) {
|
102 |
|
103 | console.log(error);
|
104 | }
|
105 | });
|
106 | });
|
107 | }
|
108 | }
|
109 |
|
110 | export function addGlobals(params: {
|
111 | source: JavaScript;
|
112 | wsPort: number;
|
113 | }): JavaScript {
|
114 | return `
|
115 | var process = self.process;
|
116 | if (!self.ZWITTERION_SOCKET && self.location.host.includes('localhost:')) {
|
117 | self.ZWITTERION_SOCKET = new WebSocket('ws://127.0.0.1:${params.wsPort}');
|
118 | self.ZWITTERION_SOCKET.addEventListener('message', (message) => {
|
119 | self.location.reload();
|
120 | });
|
121 | }
|
122 | ${params.source}
|
123 | `;
|
124 | }
|
125 |
|
126 | export function compileToJs(params: {
|
127 | source: JavaScript | TypeScript;
|
128 | jsTarget: string;
|
129 | filePath: string;
|
130 | }): JavaScript {
|
131 | const typeScriptTranspileOutput: Readonly<tsc.TranspileOutput> = tsc.transpileModule(params.source, {
|
132 | compilerOptions: {
|
133 | module: 'ES2015' as unknown as tsc.ModuleKind,
|
134 | target: params.jsTarget as any
|
135 | }
|
136 | });
|
137 |
|
138 | const babelFileResult: Readonly<babel.BabelFileResult> | null = babel.transform(typeScriptTranspileOutput.outputText, {
|
139 | 'plugins': [
|
140 | require('@babel/plugin-syntax-dynamic-import'),
|
141 | resolveBareSpecifiers(params.filePath, false),
|
142 | resolveImportPathExtensions(params.filePath)
|
143 | ]
|
144 | });
|
145 |
|
146 | if (
|
147 | babelFileResult === null ||
|
148 | babelFileResult.code === null ||
|
149 | babelFileResult.code === undefined
|
150 | ) {
|
151 | throw new Error(`Compilation error`);
|
152 | }
|
153 |
|
154 | return babelFileResult.code;
|
155 | }
|
156 |
|
157 | export async function getCustomHTTPHeadersFromFile(headersFilePath: string) {
|
158 | const headersFile: Readonly<Buffer> = await fs.readFile(headersFilePath);
|
159 | return JSON.parse(headersFile.toString());
|
160 | }
|
161 |
|
162 | export function getCustomHTTPHeadersForURL(params: {
|
163 | customHTTPHeaders: Readonly<CustomHTTPHeaders>;
|
164 | url: string;
|
165 | defaultHTTPHeaders: Readonly<HTTPHeaders>;
|
166 | }): Readonly<HTTPHeaders> {
|
167 | return Object.keys(params.customHTTPHeaders).reduce((result: Readonly<HTTPHeaders>, customHTTPHeaderRegex: string) => {
|
168 |
|
169 | if (params.url.match(customHTTPHeaderRegex)) {
|
170 | return {
|
171 | ...result,
|
172 | ...params.customHTTPHeaders[customHTTPHeaderRegex]
|
173 | };
|
174 | }
|
175 |
|
176 | return result;
|
177 | }, params.defaultHTTPHeaders);
|
178 | }
|
179 |
|
180 | export function wrapWasmInJS(binary: Readonly<Uint8Array>): JavaScript {
|
181 | return `
|
182 | //TODO perhaps there is a better way to get the ArrayBuffer that wasm needs...but for now this works
|
183 | const base64EncodedByteCode = Uint8Array.from('${binary}'.split(','));
|
184 |
|
185 | export default WebAssembly.instantiate(base64EncodedByteCode, {
|
186 | env: {
|
187 | abort: () => console.log('aborting')
|
188 | }
|
189 | }).then((result) => {
|
190 | return result.instance.exports;
|
191 | });
|
192 | `;
|
193 | } |
\ | No newline at end of file |