@valbuild/server 0.26.0 → 0.27.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.
- package/package.json +7 -4
- package/.babelrc.json +0 -5
- package/CHANGELOG.md +0 -0
- package/jest.config.js +0 -4
- package/src/LocalValServer.ts +0 -167
- package/src/ProxyValServer.ts +0 -542
- package/src/SerializedModuleContent.ts +0 -36
- package/src/Service.ts +0 -126
- package/src/ValFS.ts +0 -22
- package/src/ValFSHost.ts +0 -66
- package/src/ValModuleLoader.test.ts +0 -75
- package/src/ValModuleLoader.ts +0 -158
- package/src/ValQuickJSRuntime.ts +0 -85
- package/src/ValServer.ts +0 -24
- package/src/ValSourceFileHandler.ts +0 -57
- package/src/createFixPatch.ts +0 -170
- package/src/createRequestHandler.ts +0 -27
- package/src/expressHelpers.ts +0 -5
- package/src/getCompilerOptions.ts +0 -50
- package/src/hosting.ts +0 -290
- package/src/index.ts +0 -16
- package/src/jwt.ts +0 -93
- package/src/patch/ts/ops.test.ts +0 -937
- package/src/patch/ts/ops.ts +0 -897
- package/src/patch/ts/syntax.ts +0 -371
- package/src/patch/ts/valModule.test.ts +0 -26
- package/src/patch/ts/valModule.ts +0 -110
- package/src/patch/validation.ts +0 -81
- package/src/patchValFile.ts +0 -110
- package/src/readValFile.test.ts +0 -49
- package/src/readValFile.ts +0 -96
- package/test/example-projects/basic-next-javascript/jsconfig.json +0 -8
- package/test/example-projects/basic-next-javascript/package.json +0 -23
- package/test/example-projects/basic-next-javascript/pages/blogs.val.js +0 -20
- package/test/example-projects/basic-next-javascript/val.config.js +0 -4
- package/test/example-projects/basic-next-src-typescript/package.json +0 -23
- package/test/example-projects/basic-next-src-typescript/src/pages/blogs.val.ts +0 -20
- package/test/example-projects/basic-next-src-typescript/src/val.config.ts +0 -5
- package/test/example-projects/basic-next-src-typescript/tsconfig.json +0 -24
- package/test/example-projects/basic-next-typescript/package.json +0 -23
- package/test/example-projects/basic-next-typescript/pages/blogs.val.ts +0 -20
- package/test/example-projects/basic-next-typescript/tsconfig.json +0 -25
- package/test/example-projects/basic-next-typescript/val.config.ts +0 -5
- package/test/example-projects/typescript-description-files/README.md +0 -2
- package/test/example-projects/typescript-description-files/jsconfig.json +0 -8
- package/test/example-projects/typescript-description-files/package.json +0 -23
- package/test/example-projects/typescript-description-files/pages/blogs.val.d.ts +0 -7
- package/test/example-projects/typescript-description-files/pages/blogs.val.js +0 -19
- package/test/example-projects/typescript-description-files/val.config.d.ts +0 -3
- package/test/example-projects/typescript-description-files/val.config.js +0 -5
- package/tsconfig.json +0 -12
package/src/ValFS.ts
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
/**
|
2
|
-
* A filesystem that can update and read files.
|
3
|
-
* It does not support creating new files or directories.
|
4
|
-
*
|
5
|
-
*/
|
6
|
-
export interface ValFS {
|
7
|
-
readDirectory(
|
8
|
-
rootDir: string,
|
9
|
-
extensions: readonly string[],
|
10
|
-
excludes: readonly string[] | undefined,
|
11
|
-
includes: readonly string[],
|
12
|
-
depth?: number | undefined
|
13
|
-
): readonly string[];
|
14
|
-
|
15
|
-
writeFile(filePath: string, data: string, encoding: "binary" | "utf8"): void;
|
16
|
-
|
17
|
-
fileExists(filePath: string): boolean;
|
18
|
-
|
19
|
-
readFile(filePath: string): string | undefined;
|
20
|
-
|
21
|
-
realpath(path: string): string;
|
22
|
-
}
|
package/src/ValFSHost.ts
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
import path from "path";
|
2
|
-
import ts from "typescript";
|
3
|
-
import { ValFS } from "./ValFS";
|
4
|
-
|
5
|
-
/**
|
6
|
-
* An implementation of methods in the various ts.*Host interfaces
|
7
|
-
* that uses ValFS to resolve modules and read/write files.
|
8
|
-
*/
|
9
|
-
export interface IValFSHost
|
10
|
-
extends ts.ParseConfigHost,
|
11
|
-
ts.ModuleResolutionHost {
|
12
|
-
useCaseSensitiveFileNames: boolean;
|
13
|
-
|
14
|
-
writeFile(fileName: string, text: string, encoding: "binary" | "utf8"): void;
|
15
|
-
}
|
16
|
-
|
17
|
-
export class ValFSHost implements IValFSHost {
|
18
|
-
constructor(
|
19
|
-
protected readonly valFS: ValFS,
|
20
|
-
protected readonly currentDirectory: string
|
21
|
-
) {}
|
22
|
-
|
23
|
-
useCaseSensitiveFileNames = true;
|
24
|
-
readDirectory(
|
25
|
-
rootDir: string,
|
26
|
-
extensions: readonly string[],
|
27
|
-
excludes: readonly string[] | undefined,
|
28
|
-
includes: readonly string[],
|
29
|
-
depth?: number | undefined
|
30
|
-
): readonly string[] {
|
31
|
-
return this.valFS.readDirectory(
|
32
|
-
rootDir,
|
33
|
-
extensions,
|
34
|
-
excludes,
|
35
|
-
includes,
|
36
|
-
depth
|
37
|
-
);
|
38
|
-
}
|
39
|
-
|
40
|
-
writeFile(fileName: string, text: string, encoding: "binary" | "utf8"): void {
|
41
|
-
this.valFS.writeFile(fileName, text, encoding);
|
42
|
-
}
|
43
|
-
|
44
|
-
getCurrentDirectory(): string {
|
45
|
-
return this.currentDirectory;
|
46
|
-
}
|
47
|
-
|
48
|
-
getCanonicalFileName(fileName: string): string {
|
49
|
-
if (path.isAbsolute(fileName)) {
|
50
|
-
return path.normalize(fileName);
|
51
|
-
}
|
52
|
-
return path.resolve(this.getCurrentDirectory(), fileName);
|
53
|
-
}
|
54
|
-
|
55
|
-
fileExists(fileName: string): boolean {
|
56
|
-
return this.valFS.fileExists(fileName);
|
57
|
-
}
|
58
|
-
|
59
|
-
readFile(fileName: string): string | undefined {
|
60
|
-
return this.valFS.readFile(fileName);
|
61
|
-
}
|
62
|
-
|
63
|
-
realpath(path: string): string {
|
64
|
-
return this.valFS.realpath(path);
|
65
|
-
}
|
66
|
-
}
|
@@ -1,75 +0,0 @@
|
|
1
|
-
import { createModuleLoader } from "./ValModuleLoader";
|
2
|
-
import path from "path";
|
3
|
-
|
4
|
-
const TestCaseDir = "../test/example-projects";
|
5
|
-
const TestCases = [
|
6
|
-
{ name: "basic-next-typescript", valConfigDir: ".", ext: "ts" },
|
7
|
-
{
|
8
|
-
name: "basic-next-src-typescript",
|
9
|
-
valConfigDir: "./src",
|
10
|
-
ext: "ts",
|
11
|
-
},
|
12
|
-
{ name: "basic-next-javascript", valConfigDir: ".", ext: "js" },
|
13
|
-
{ name: "typescript-description-files", valConfigDir: ".", ext: "js" },
|
14
|
-
];
|
15
|
-
|
16
|
-
describe("val module loader", () => {
|
17
|
-
test.each(TestCases)(
|
18
|
-
"resolution and smoke test transpilation for: $name",
|
19
|
-
async (testCase) => {
|
20
|
-
const rootDir = path.resolve(__dirname, TestCaseDir, testCase.name);
|
21
|
-
const loader = createModuleLoader(rootDir);
|
22
|
-
expect(
|
23
|
-
await loader.getModule(
|
24
|
-
loader.resolveModulePath(
|
25
|
-
`${testCase.valConfigDir}/val-system.${testCase.ext}`,
|
26
|
-
"./pages/blogs.val"
|
27
|
-
)
|
28
|
-
)
|
29
|
-
).toContain("/pages/blogs");
|
30
|
-
expect(
|
31
|
-
await loader.getModule(
|
32
|
-
loader.resolveModulePath(
|
33
|
-
`${testCase.valConfigDir}/pages/blogs.val.${testCase.ext}`,
|
34
|
-
"../val.config"
|
35
|
-
)
|
36
|
-
)
|
37
|
-
).toContain("@valbuild/core");
|
38
|
-
}
|
39
|
-
);
|
40
|
-
|
41
|
-
test("resolution based on baseDir / paths in tsconfig", () => {
|
42
|
-
const rootDir = path.resolve(
|
43
|
-
__dirname,
|
44
|
-
TestCaseDir,
|
45
|
-
"basic-next-src-typescript"
|
46
|
-
);
|
47
|
-
const moduleLoader = createModuleLoader(rootDir);
|
48
|
-
|
49
|
-
const containingFile = "./src/pages/blogs.val.ts";
|
50
|
-
const baseCase = moduleLoader.resolveModulePath(
|
51
|
-
containingFile,
|
52
|
-
"../val.config"
|
53
|
-
); // tsconfig maps @ to src
|
54
|
-
const pathsMapping = moduleLoader.resolveModulePath(
|
55
|
-
containingFile,
|
56
|
-
"@/val.config"
|
57
|
-
); // tsconfig maps @ to src
|
58
|
-
expect(baseCase).toBeDefined();
|
59
|
-
expect(baseCase).toEqual(pathsMapping);
|
60
|
-
});
|
61
|
-
|
62
|
-
test("resolution on .d.ts files", () => {
|
63
|
-
const rootDir = path.resolve(
|
64
|
-
__dirname,
|
65
|
-
TestCaseDir,
|
66
|
-
"typescript-description-files"
|
67
|
-
);
|
68
|
-
const moduleLoader = createModuleLoader(rootDir);
|
69
|
-
|
70
|
-
const containingFile = "./pages/blogs.val.js";
|
71
|
-
expect(
|
72
|
-
moduleLoader.resolveModulePath(containingFile, "../val.config")
|
73
|
-
).toMatch(/val\.config\.js$/);
|
74
|
-
});
|
75
|
-
});
|
package/src/ValModuleLoader.ts
DELETED
@@ -1,158 +0,0 @@
|
|
1
|
-
import ts from "typescript";
|
2
|
-
import { getCompilerOptions } from "./getCompilerOptions";
|
3
|
-
import type { IValFSHost } from "./ValFSHost";
|
4
|
-
import { ValSourceFileHandler } from "./ValSourceFileHandler";
|
5
|
-
import fs from "fs";
|
6
|
-
import { transform } from "sucrase";
|
7
|
-
const JsFileLookupMapping: [resolvedFileExt: string, replacements: string[]][] =
|
8
|
-
[
|
9
|
-
// NOTE: first one matching will be used
|
10
|
-
[".cjs.d.ts", [".esm.js", ".mjs.js"]],
|
11
|
-
[".cjs.js", [".esm.js", ".mjs.js"]],
|
12
|
-
[".cjs", [".mjs"]],
|
13
|
-
[".d.ts", [".js", ".esm.js", ".mjs.js"]],
|
14
|
-
];
|
15
|
-
|
16
|
-
export const createModuleLoader = (
|
17
|
-
rootDir: string,
|
18
|
-
host: IValFSHost = {
|
19
|
-
...ts.sys,
|
20
|
-
writeFile: fs.writeFileSync,
|
21
|
-
}
|
22
|
-
): ValModuleLoader => {
|
23
|
-
const compilerOptions = getCompilerOptions(rootDir, host);
|
24
|
-
const sourceFileHandler = new ValSourceFileHandler(
|
25
|
-
rootDir,
|
26
|
-
compilerOptions,
|
27
|
-
host
|
28
|
-
);
|
29
|
-
const loader = new ValModuleLoader(
|
30
|
-
rootDir,
|
31
|
-
compilerOptions,
|
32
|
-
sourceFileHandler,
|
33
|
-
host
|
34
|
-
);
|
35
|
-
return loader;
|
36
|
-
};
|
37
|
-
|
38
|
-
const MAX_CACHE_SIZE = 100 * 1024 * 1024; // 100 mb
|
39
|
-
const MAX_OBJECT_KEY_SIZE = 2 ** 27; // https://stackoverflow.com/questions/13367391/is-there-a-limit-on-length-of-the-key-string-in-js-object
|
40
|
-
|
41
|
-
export class ValModuleLoader {
|
42
|
-
private cache: Record<string, string>;
|
43
|
-
private cacheSize: number;
|
44
|
-
constructor(
|
45
|
-
public readonly projectRoot: string,
|
46
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
47
|
-
private readonly compilerOptions: ts.CompilerOptions,
|
48
|
-
private readonly sourceFileHandler: ValSourceFileHandler,
|
49
|
-
private readonly host: IValFSHost = {
|
50
|
-
...ts.sys,
|
51
|
-
writeFile: fs.writeFileSync,
|
52
|
-
},
|
53
|
-
private readonly disableCache: boolean = false
|
54
|
-
) {
|
55
|
-
this.cache = {};
|
56
|
-
this.cacheSize = 0;
|
57
|
-
}
|
58
|
-
|
59
|
-
getModule(modulePath: string): string {
|
60
|
-
if (!modulePath) {
|
61
|
-
throw Error(`Illegal module path: "${modulePath}"`);
|
62
|
-
}
|
63
|
-
const code = this.host.readFile(modulePath);
|
64
|
-
if (!code) {
|
65
|
-
throw Error(`Could not read file "${modulePath}"`);
|
66
|
-
}
|
67
|
-
let compiledCode;
|
68
|
-
if (this.cache[code] && !this.disableCache) {
|
69
|
-
// TODO: use hash instead of code as key
|
70
|
-
compiledCode = this.cache[code];
|
71
|
-
} else {
|
72
|
-
compiledCode = transform(code, {
|
73
|
-
filePath: modulePath,
|
74
|
-
disableESTransforms: true,
|
75
|
-
transforms: ["typescript"],
|
76
|
-
}).code;
|
77
|
-
if (!this.disableCache) {
|
78
|
-
if (this.cacheSize > MAX_CACHE_SIZE) {
|
79
|
-
console.warn("Cache size exceeded, clearing cache");
|
80
|
-
this.cache = {};
|
81
|
-
this.cacheSize = 0;
|
82
|
-
}
|
83
|
-
if (code.length < MAX_OBJECT_KEY_SIZE) {
|
84
|
-
this.cache[code] = compiledCode;
|
85
|
-
this.cacheSize += code.length + compiledCode.length; // code is mostly ASCII so 1 byte per char
|
86
|
-
}
|
87
|
-
}
|
88
|
-
}
|
89
|
-
return compiledCode;
|
90
|
-
}
|
91
|
-
|
92
|
-
resolveModulePath(
|
93
|
-
containingFilePath: string,
|
94
|
-
requestedModuleName: string
|
95
|
-
): string {
|
96
|
-
let sourceFileName = this.sourceFileHandler.resolveSourceModulePath(
|
97
|
-
containingFilePath,
|
98
|
-
requestedModuleName
|
99
|
-
);
|
100
|
-
|
101
|
-
if (requestedModuleName === "@vercel/stega") {
|
102
|
-
sourceFileName = this.sourceFileHandler
|
103
|
-
.resolveSourceModulePath(containingFilePath, "@vercel/stega")
|
104
|
-
.replace("stega/dist", "stega/dist/esm");
|
105
|
-
}
|
106
|
-
const matches = this.findMatchingJsFile(sourceFileName);
|
107
|
-
if (matches.match === false) {
|
108
|
-
throw Error(
|
109
|
-
`Could not find matching js file for module "${requestedModuleName}" requested by: "${containingFilePath}". Tried:\n${matches.tried.join(
|
110
|
-
"\n"
|
111
|
-
)}`
|
112
|
-
);
|
113
|
-
}
|
114
|
-
const filePath = matches.match;
|
115
|
-
// resolve all symlinks (preconstruct for example symlinks the dist folder)
|
116
|
-
const followedPath = this.host.realpath?.(filePath) ?? filePath;
|
117
|
-
if (!followedPath) {
|
118
|
-
throw Error(
|
119
|
-
`File path was empty: "${filePath}", containing file: "${containingFilePath}", requested module: "${requestedModuleName}"`
|
120
|
-
);
|
121
|
-
}
|
122
|
-
return followedPath;
|
123
|
-
}
|
124
|
-
|
125
|
-
private findMatchingJsFile(
|
126
|
-
filePath: string
|
127
|
-
): { match: string } | { match: false; tried: string[] } {
|
128
|
-
let requiresReplacements = false;
|
129
|
-
for (const [currentEnding] of JsFileLookupMapping) {
|
130
|
-
if (filePath.endsWith(currentEnding)) {
|
131
|
-
requiresReplacements = true;
|
132
|
-
break;
|
133
|
-
}
|
134
|
-
}
|
135
|
-
// avoid unnecessary calls to fileExists if we don't need to replace anything
|
136
|
-
if (!requiresReplacements) {
|
137
|
-
if (this.host.fileExists(filePath)) {
|
138
|
-
return { match: filePath };
|
139
|
-
}
|
140
|
-
}
|
141
|
-
const tried = [];
|
142
|
-
for (const [currentEnding, replacements] of JsFileLookupMapping) {
|
143
|
-
if (filePath.endsWith(currentEnding)) {
|
144
|
-
for (const replacement of replacements) {
|
145
|
-
const newFilePath =
|
146
|
-
filePath.slice(0, -currentEnding.length) + replacement;
|
147
|
-
if (this.host.fileExists(newFilePath)) {
|
148
|
-
return { match: newFilePath };
|
149
|
-
} else {
|
150
|
-
tried.push(newFilePath);
|
151
|
-
}
|
152
|
-
}
|
153
|
-
}
|
154
|
-
}
|
155
|
-
|
156
|
-
return { match: false, tried: tried.concat(filePath) };
|
157
|
-
}
|
158
|
-
}
|
package/src/ValQuickJSRuntime.ts
DELETED
@@ -1,85 +0,0 @@
|
|
1
|
-
import { JSModuleNormalizeResult, QuickJSWASMModule } from "quickjs-emscripten";
|
2
|
-
import { ValModuleLoader } from "./ValModuleLoader";
|
3
|
-
|
4
|
-
export async function newValQuickJSRuntime(
|
5
|
-
quickJSModule: Pick<QuickJSWASMModule, "newRuntime">,
|
6
|
-
moduleLoader: ValModuleLoader,
|
7
|
-
{
|
8
|
-
maxStackSize = 1024 * 640, // TODO: these were randomly chosen, we should figure out what the right values are:
|
9
|
-
memoryLimit = 1024 * 640,
|
10
|
-
}: {
|
11
|
-
maxStackSize?: number;
|
12
|
-
memoryLimit?: number;
|
13
|
-
} = {}
|
14
|
-
) {
|
15
|
-
const runtime = quickJSModule.newRuntime();
|
16
|
-
|
17
|
-
runtime.setMaxStackSize(maxStackSize);
|
18
|
-
runtime.setMemoryLimit(memoryLimit);
|
19
|
-
|
20
|
-
runtime.setModuleLoader(
|
21
|
-
(modulePath) => {
|
22
|
-
try {
|
23
|
-
// Special cases to avoid loading the React packages since currently React does not have a ESM build:
|
24
|
-
// TODO: this is not stable, find a better way to do this
|
25
|
-
if (modulePath === "@valbuild/react") {
|
26
|
-
return {
|
27
|
-
value:
|
28
|
-
"export const useVal = () => { throw Error(`Cannot use 'useVal' in this type of file`) }; export function ValProvider() { throw Error(`Cannot use 'ValProvider' in this type of file`) }; export function ValRichText() { throw Error(`Cannot use 'ValRichText' in this type of file`)};",
|
29
|
-
};
|
30
|
-
}
|
31
|
-
if (modulePath === "@valbuild/react/stega") {
|
32
|
-
return {
|
33
|
-
value:
|
34
|
-
"export const useVal = () => { throw Error(`Cannot use 'useVal' in this type of file`) }; export const fetchVal = () => { throw Error(`Cannot use 'fetchVal' in this type of file`) }; export const autoTagJSX = () => { /* ignore */ };",
|
35
|
-
};
|
36
|
-
}
|
37
|
-
if (modulePath.startsWith("next")) {
|
38
|
-
return {
|
39
|
-
value:
|
40
|
-
"export default new Proxy({}, { get() { return () => { throw new Error(`Cannot import 'next' in this file`) } } } )",
|
41
|
-
};
|
42
|
-
}
|
43
|
-
if (modulePath.startsWith("react")) {
|
44
|
-
return {
|
45
|
-
value:
|
46
|
-
"export default new Proxy({}, { get() { return () => { throw new Error(`Cannot import 'react' in this file`) } } } )",
|
47
|
-
};
|
48
|
-
}
|
49
|
-
return { value: moduleLoader.getModule(modulePath) };
|
50
|
-
} catch (e) {
|
51
|
-
return {
|
52
|
-
error: Error(`Could not resolve module: '${modulePath}': `),
|
53
|
-
};
|
54
|
-
}
|
55
|
-
},
|
56
|
-
(baseModuleName, requestedName): JSModuleNormalizeResult => {
|
57
|
-
try {
|
58
|
-
if (requestedName === "@valbuild/react") {
|
59
|
-
return { value: requestedName };
|
60
|
-
}
|
61
|
-
if (requestedName === "@valbuild/react/stega") {
|
62
|
-
return { value: requestedName };
|
63
|
-
}
|
64
|
-
if (requestedName.startsWith("next")) {
|
65
|
-
return { value: requestedName };
|
66
|
-
}
|
67
|
-
if (requestedName.startsWith("react")) {
|
68
|
-
return { value: requestedName };
|
69
|
-
}
|
70
|
-
const modulePath = moduleLoader.resolveModulePath(
|
71
|
-
baseModuleName,
|
72
|
-
requestedName
|
73
|
-
);
|
74
|
-
return { value: modulePath };
|
75
|
-
} catch (e) {
|
76
|
-
console.debug(
|
77
|
-
`Could not resolve ${requestedName} in ${baseModuleName}`,
|
78
|
-
e
|
79
|
-
);
|
80
|
-
return { value: requestedName };
|
81
|
-
}
|
82
|
-
}
|
83
|
-
);
|
84
|
-
return runtime;
|
85
|
-
}
|
package/src/ValServer.ts
DELETED
@@ -1,24 +0,0 @@
|
|
1
|
-
import express from "express";
|
2
|
-
|
3
|
-
export interface ValServer {
|
4
|
-
authorize(req: express.Request, res: express.Response): Promise<void>;
|
5
|
-
|
6
|
-
callback(req: express.Request, res: express.Response): Promise<void>;
|
7
|
-
|
8
|
-
logout(req: express.Request, res: express.Response): Promise<void>;
|
9
|
-
|
10
|
-
session(req: express.Request, res: express.Response): Promise<void>;
|
11
|
-
|
12
|
-
postPatches(
|
13
|
-
req: express.Request<{ 0: string }>,
|
14
|
-
res: express.Response
|
15
|
-
): Promise<void>;
|
16
|
-
|
17
|
-
commit(req: express.Request, res: express.Response): Promise<void>;
|
18
|
-
|
19
|
-
enable(req: express.Request, res: express.Response): Promise<void>;
|
20
|
-
disable(req: express.Request, res: express.Response): Promise<void>;
|
21
|
-
|
22
|
-
getTree(req: express.Request, res: express.Response): Promise<void>;
|
23
|
-
getFiles(req: express.Request, res: express.Response): Promise<void>;
|
24
|
-
}
|
@@ -1,57 +0,0 @@
|
|
1
|
-
import ts from "typescript";
|
2
|
-
import path from "path";
|
3
|
-
import { IValFSHost } from "./ValFSHost";
|
4
|
-
import fs from "fs";
|
5
|
-
|
6
|
-
export class ValSourceFileHandler {
|
7
|
-
constructor(
|
8
|
-
private readonly projectRoot: string,
|
9
|
-
private readonly compilerOptions: ts.CompilerOptions,
|
10
|
-
private readonly host: IValFSHost = {
|
11
|
-
...ts.sys,
|
12
|
-
writeFile: fs.writeFileSync,
|
13
|
-
}
|
14
|
-
) {}
|
15
|
-
|
16
|
-
getSourceFile(filePath: string): ts.SourceFile | undefined {
|
17
|
-
const fileContent = this.host.readFile(filePath);
|
18
|
-
const scriptTarget = this.compilerOptions.target ?? ts.ScriptTarget.ES2020;
|
19
|
-
if (fileContent) {
|
20
|
-
return ts.createSourceFile(filePath, fileContent, scriptTarget);
|
21
|
-
}
|
22
|
-
}
|
23
|
-
|
24
|
-
writeSourceFile(sourceFile: ts.SourceFile) {
|
25
|
-
return this.writeFile(sourceFile.fileName, sourceFile.text, "utf8");
|
26
|
-
}
|
27
|
-
|
28
|
-
writeFile(filePath: string, content: string, encoding: "binary" | "utf8") {
|
29
|
-
this.host.writeFile(filePath, content, encoding);
|
30
|
-
}
|
31
|
-
|
32
|
-
resolveSourceModulePath(
|
33
|
-
containingFilePath: string,
|
34
|
-
requestedModuleName: string
|
35
|
-
): string {
|
36
|
-
const resolutionRes = ts.resolveModuleName(
|
37
|
-
requestedModuleName,
|
38
|
-
path.isAbsolute(containingFilePath)
|
39
|
-
? containingFilePath
|
40
|
-
: path.resolve(this.projectRoot, containingFilePath),
|
41
|
-
this.compilerOptions,
|
42
|
-
this.host,
|
43
|
-
undefined,
|
44
|
-
undefined,
|
45
|
-
ts.ModuleKind.ESNext
|
46
|
-
);
|
47
|
-
const resolvedModule = resolutionRes.resolvedModule;
|
48
|
-
if (!resolvedModule) {
|
49
|
-
throw Error(
|
50
|
-
`Could not resolve module "${requestedModuleName}", base: "${containingFilePath}": No resolved modules returned: ${JSON.stringify(
|
51
|
-
resolutionRes
|
52
|
-
)}`
|
53
|
-
);
|
54
|
-
}
|
55
|
-
return resolvedModule.resolvedFileName;
|
56
|
-
}
|
57
|
-
}
|
package/src/createFixPatch.ts
DELETED
@@ -1,170 +0,0 @@
|
|
1
|
-
import {
|
2
|
-
FILE_REF_PROP,
|
3
|
-
Internal,
|
4
|
-
SourcePath,
|
5
|
-
ValidationError,
|
6
|
-
} from "@valbuild/core";
|
7
|
-
import { Patch, sourceToPatchPath } from "@valbuild/core/patch";
|
8
|
-
import sizeOf from "image-size";
|
9
|
-
import path from "path";
|
10
|
-
import fs from "fs";
|
11
|
-
|
12
|
-
// TODO: find a better name? transformFixesToPatch?
|
13
|
-
export async function createFixPatch(
|
14
|
-
config: { projectRoot: string },
|
15
|
-
apply: boolean,
|
16
|
-
sourcePath: SourcePath,
|
17
|
-
validationError: ValidationError
|
18
|
-
): Promise<{ patch: Patch; remainingErrors: ValidationError[] } | undefined> {
|
19
|
-
async function getImageMetadata() {
|
20
|
-
const maybeRef =
|
21
|
-
validationError.value &&
|
22
|
-
typeof validationError.value === "object" &&
|
23
|
-
FILE_REF_PROP in validationError.value &&
|
24
|
-
typeof validationError.value[FILE_REF_PROP] === "string"
|
25
|
-
? validationError.value[FILE_REF_PROP]
|
26
|
-
: undefined;
|
27
|
-
|
28
|
-
if (!maybeRef) {
|
29
|
-
// TODO:
|
30
|
-
throw Error("Cannot fix image without a file reference");
|
31
|
-
}
|
32
|
-
const localFile = path.join(config.projectRoot, maybeRef);
|
33
|
-
const buffer = fs.readFileSync(localFile);
|
34
|
-
const sha256 = await Internal.getSHA256Hash(buffer);
|
35
|
-
const imageSize = sizeOf(buffer);
|
36
|
-
return {
|
37
|
-
...imageSize,
|
38
|
-
sha256,
|
39
|
-
};
|
40
|
-
}
|
41
|
-
const remainingErrors: ValidationError[] = [];
|
42
|
-
const patch: Patch = [];
|
43
|
-
for (const fix of validationError.fixes || []) {
|
44
|
-
if (fix === "image:replace-metadata" || fix === "image:add-metadata") {
|
45
|
-
const imageMetadata = await getImageMetadata();
|
46
|
-
if (
|
47
|
-
imageMetadata.width === undefined ||
|
48
|
-
imageMetadata.height === undefined ||
|
49
|
-
imageMetadata.sha256 === undefined
|
50
|
-
) {
|
51
|
-
remainingErrors.push({
|
52
|
-
...validationError,
|
53
|
-
message: "Failed to get image metadata",
|
54
|
-
fixes: undefined,
|
55
|
-
});
|
56
|
-
} else if (fix === "image:replace-metadata") {
|
57
|
-
const currentValue = validationError.value;
|
58
|
-
const metadataIsCorrect =
|
59
|
-
// metadata is a prop that is an object
|
60
|
-
typeof currentValue === "object" &&
|
61
|
-
currentValue &&
|
62
|
-
"metadata" in currentValue &&
|
63
|
-
currentValue.metadata &&
|
64
|
-
typeof currentValue.metadata === "object" &&
|
65
|
-
// sha256 is correct
|
66
|
-
"sha256" in currentValue.metadata &&
|
67
|
-
currentValue.metadata.sha256 === imageMetadata.sha256 &&
|
68
|
-
// width is correct
|
69
|
-
"width" in currentValue.metadata &&
|
70
|
-
currentValue.metadata.width === imageMetadata.width &&
|
71
|
-
// height is correct
|
72
|
-
"height" in currentValue.metadata &&
|
73
|
-
currentValue.metadata.height === imageMetadata.height;
|
74
|
-
|
75
|
-
// skips if the metadata is already correct
|
76
|
-
if (!metadataIsCorrect) {
|
77
|
-
if (apply) {
|
78
|
-
patch.push({
|
79
|
-
op: "replace",
|
80
|
-
path: sourceToPatchPath(sourcePath).concat("metadata"),
|
81
|
-
value: {
|
82
|
-
width: imageMetadata.width,
|
83
|
-
height: imageMetadata.height,
|
84
|
-
sha256: imageMetadata.sha256,
|
85
|
-
},
|
86
|
-
});
|
87
|
-
} else {
|
88
|
-
if (
|
89
|
-
typeof currentValue === "object" &&
|
90
|
-
currentValue &&
|
91
|
-
"metadata" in currentValue &&
|
92
|
-
currentValue.metadata &&
|
93
|
-
typeof currentValue.metadata === "object"
|
94
|
-
) {
|
95
|
-
if (
|
96
|
-
!("sha256" in currentValue.metadata) ||
|
97
|
-
currentValue.metadata.sha256 !== imageMetadata.sha256
|
98
|
-
) {
|
99
|
-
remainingErrors.push({
|
100
|
-
message:
|
101
|
-
"Image metadata sha256 is incorrect! Found: " +
|
102
|
-
("sha256" in currentValue.metadata
|
103
|
-
? currentValue.metadata.sha256
|
104
|
-
: "<empty>") +
|
105
|
-
". Expected: " +
|
106
|
-
imageMetadata.sha256 +
|
107
|
-
".",
|
108
|
-
fixes: undefined,
|
109
|
-
});
|
110
|
-
}
|
111
|
-
if (
|
112
|
-
!("width" in currentValue.metadata) ||
|
113
|
-
currentValue.metadata.width !== imageMetadata.width
|
114
|
-
) {
|
115
|
-
remainingErrors.push({
|
116
|
-
message:
|
117
|
-
"Image metadata width is incorrect! Found: " +
|
118
|
-
("width" in currentValue.metadata
|
119
|
-
? currentValue.metadata.width
|
120
|
-
: "<empty>") +
|
121
|
-
". Expected: " +
|
122
|
-
imageMetadata.width,
|
123
|
-
fixes: undefined,
|
124
|
-
});
|
125
|
-
}
|
126
|
-
if (
|
127
|
-
!("height" in currentValue.metadata) ||
|
128
|
-
currentValue.metadata.height !== imageMetadata.height
|
129
|
-
) {
|
130
|
-
remainingErrors.push({
|
131
|
-
message:
|
132
|
-
"Image metadata height is incorrect! Found: " +
|
133
|
-
("height" in currentValue.metadata
|
134
|
-
? currentValue.metadata.height
|
135
|
-
: "<empty>") +
|
136
|
-
". Expected: " +
|
137
|
-
imageMetadata.height,
|
138
|
-
fixes: undefined,
|
139
|
-
});
|
140
|
-
}
|
141
|
-
} else {
|
142
|
-
remainingErrors.push({
|
143
|
-
...validationError,
|
144
|
-
message: "Image metadata is not an object!",
|
145
|
-
fixes: undefined,
|
146
|
-
});
|
147
|
-
}
|
148
|
-
}
|
149
|
-
}
|
150
|
-
} else if (fix === "image:add-metadata") {
|
151
|
-
patch.push({
|
152
|
-
op: "add",
|
153
|
-
path: sourceToPatchPath(sourcePath).concat("metadata"),
|
154
|
-
value: {
|
155
|
-
width: imageMetadata.width,
|
156
|
-
height: imageMetadata.height,
|
157
|
-
sha256: imageMetadata.sha256,
|
158
|
-
},
|
159
|
-
});
|
160
|
-
}
|
161
|
-
}
|
162
|
-
}
|
163
|
-
if (!validationError.fixes || validationError.fixes.length === 0) {
|
164
|
-
remainingErrors.push(validationError);
|
165
|
-
}
|
166
|
-
return {
|
167
|
-
patch,
|
168
|
-
remainingErrors,
|
169
|
-
};
|
170
|
-
}
|
@@ -1,27 +0,0 @@
|
|
1
|
-
import express, { RequestHandler, Router } from "express";
|
2
|
-
import { ValServer } from "./ValServer";
|
3
|
-
import { createRequestHandler as createUIRequestHandler } from "@valbuild/ui/server";
|
4
|
-
|
5
|
-
export function createRequestHandler(valServer: ValServer): RequestHandler {
|
6
|
-
const router = Router();
|
7
|
-
|
8
|
-
router.use("/static", createUIRequestHandler());
|
9
|
-
router.get("/session", valServer.session.bind(valServer));
|
10
|
-
router.get("/authorize", valServer.authorize.bind(valServer));
|
11
|
-
router.get("/callback", valServer.callback.bind(valServer));
|
12
|
-
router.get("/logout", valServer.logout.bind(valServer));
|
13
|
-
router.post<{ 0: string }>(
|
14
|
-
"/patches/*",
|
15
|
-
express.json({
|
16
|
-
type: "application/json",
|
17
|
-
limit: "10mb",
|
18
|
-
}),
|
19
|
-
valServer.postPatches.bind(valServer)
|
20
|
-
);
|
21
|
-
router.post("/commit", valServer.commit.bind(valServer));
|
22
|
-
router.get("/enable", valServer.enable.bind(valServer));
|
23
|
-
router.get("/disable", valServer.disable.bind(valServer));
|
24
|
-
router.get("/tree/*", valServer.getTree.bind(valServer));
|
25
|
-
router.get("/files/*", valServer.getFiles.bind(valServer));
|
26
|
-
return router;
|
27
|
-
}
|