mirror of
https://github.com/lowdefy/lowdefy.git
synced 2025-01-06 13:15:24 +08:00
feat(cli): add errorBoundary and getLowdefyVersion utils
This commit is contained in:
parent
88a126029c
commit
519e604771
1
.pnp.js
generated
1
.pnp.js
generated
@ -3713,6 +3713,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) {
|
||||
["commander", "npm:6.1.0"],
|
||||
["inquirer", "npm:7.3.3"],
|
||||
["jest", "npm:26.5.3"],
|
||||
["js-yaml", "npm:3.14.0"],
|
||||
["webpack", "virtual:1e43113c7dc84a5d03308bf7ffaf00574d351ca16282af6c6c0b9576804fb03914bdf2200961292f439926b2e537dce172d7529f79013ce51b9f2d56e9cd836b#npm:5.1.3"],
|
||||
["webpack-cli", "virtual:1e43113c7dc84a5d03308bf7ffaf00574d351ca16282af6c6c0b9576804fb03914bdf2200961292f439926b2e537dce172d7529f79013ce51b9f2d56e9cd836b#npm:4.0.0"]
|
||||
],
|
||||
|
@ -1,14 +0,0 @@
|
||||
|
||||
types:
|
||||
Context:
|
||||
url: http://localhost:3002/meta/Context.json
|
||||
Button:
|
||||
url: http://localhost:3002/meta/Button.json
|
||||
|
||||
|
||||
pages:
|
||||
- id: '1'
|
||||
type: Context
|
||||
blocks:
|
||||
- id: button
|
||||
type: Button
|
@ -16,6 +16,7 @@
|
||||
|
||||
function testContext({ artifactSetter, configLoader, logger = {}, metaLoader } = {}) {
|
||||
const defaultLogger = {
|
||||
info: () => {},
|
||||
log: () => {},
|
||||
warn: () => {},
|
||||
error: () => {},
|
||||
|
@ -46,7 +46,8 @@
|
||||
"chalk": "4.1.0",
|
||||
"chokidar": "3.4.2",
|
||||
"commander": "6.1.0",
|
||||
"inquirer": "7.3.3"
|
||||
"inquirer": "7.3.3",
|
||||
"js-yaml": "3.14.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "7.12.1",
|
||||
|
@ -16,13 +16,18 @@
|
||||
|
||||
import path from 'path';
|
||||
import buildScript from '@lowdefy/build';
|
||||
import createPrint from '../print';
|
||||
import createPrint from '../../utils/print';
|
||||
import getLowdefyVersion from '../../utils/getLowdefyVersion';
|
||||
import errorBoundary from '../../utils/errorBoundary';
|
||||
|
||||
function build(program) {
|
||||
async function build(program) {
|
||||
let baseDirectory = process.cwd();
|
||||
if (program.baseDirectory) {
|
||||
baseDirectory = path.resolve(program.baseDirectory);
|
||||
}
|
||||
const version = await getLowdefyVersion(program.baseDirectory);
|
||||
console.log(version);
|
||||
|
||||
buildScript({
|
||||
logger: createPrint({ timestamp: true }),
|
||||
cacheDirectory: path.resolve(baseDirectory, '.lowdefy/.cache'),
|
||||
@ -31,4 +36,4 @@ function build(program) {
|
||||
});
|
||||
}
|
||||
|
||||
export default build;
|
||||
export default errorBoundary(build);
|
@ -16,7 +16,7 @@
|
||||
|
||||
import program from 'commander';
|
||||
import packageJson from '../package.json';
|
||||
import build from './commands/build';
|
||||
import build from './commands/build/build.js';
|
||||
|
||||
const { description, version } = packageJson;
|
||||
|
||||
|
35
packages/cli/src/utils/errorBoundary.js
Normal file
35
packages/cli/src/utils/errorBoundary.js
Normal file
@ -0,0 +1,35 @@
|
||||
/*
|
||||
Copyright 2020 Lowdefy, Inc
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import createPrint from './print';
|
||||
|
||||
function errorBoundary(fn, options = {}) {
|
||||
async function run(...args) {
|
||||
try {
|
||||
const res = await fn(...args);
|
||||
return res;
|
||||
} catch (error) {
|
||||
const print = createPrint();
|
||||
print.error(error.message);
|
||||
if (!options.stayAlive) {
|
||||
process.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
return run;
|
||||
}
|
||||
|
||||
export default errorBoundary;
|
117
packages/cli/src/utils/errorBoundary.test.js
Normal file
117
packages/cli/src/utils/errorBoundary.test.js
Normal file
@ -0,0 +1,117 @@
|
||||
/*
|
||||
Copyright 2020 Lowdefy, Inc
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import errorBoundary from './errorBoundary';
|
||||
import createPrint from './print';
|
||||
|
||||
jest.mock('./print', () => {
|
||||
const error = jest.fn();
|
||||
return () => ({
|
||||
error,
|
||||
});
|
||||
});
|
||||
|
||||
const print = createPrint();
|
||||
|
||||
async function wait(ms) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
print.error.mockReset();
|
||||
});
|
||||
|
||||
test('Error boundary with synchronous function', async () => {
|
||||
const fn = jest.fn(() => 1 + 1);
|
||||
const wrapped = errorBoundary(fn);
|
||||
const res = await wrapped();
|
||||
expect(res).toBe(2);
|
||||
expect(fn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('Error boundary with asynchronous function', async () => {
|
||||
const fn = jest.fn(async () => {
|
||||
await wait(3);
|
||||
return 4;
|
||||
});
|
||||
const wrapped = errorBoundary(fn);
|
||||
const res = await wrapped();
|
||||
expect(res).toBe(4);
|
||||
expect(fn).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('Pass args to synchronous function', async () => {
|
||||
const fn = jest.fn((arg1, arg2) => ({ arg1, arg2 }));
|
||||
const wrapped = errorBoundary(fn);
|
||||
const res = await wrapped('1', '2');
|
||||
expect(res).toEqual({ arg1: '1', arg2: '2' });
|
||||
});
|
||||
|
||||
test('Catch error synchronous function, stay alive', async () => {
|
||||
const fn = jest.fn(() => {
|
||||
throw new Error('Error');
|
||||
});
|
||||
const wrapped = errorBoundary(fn, { stayAlive: true });
|
||||
const res = await wrapped();
|
||||
expect(res).toBe(undefined);
|
||||
expect(fn).toHaveBeenCalled();
|
||||
expect(print.error.mock.calls).toEqual([['Error']]);
|
||||
});
|
||||
|
||||
test('Catch error asynchronous function, stay alive', async () => {
|
||||
const fn = jest.fn(async () => {
|
||||
await wait(3);
|
||||
throw new Error('Async Error');
|
||||
});
|
||||
const wrapped = errorBoundary(fn, { stayAlive: true });
|
||||
const res = await wrapped();
|
||||
expect(res).toBe(undefined);
|
||||
expect(fn).toHaveBeenCalled();
|
||||
expect(print.error.mock.calls).toEqual([['Async Error']]);
|
||||
});
|
||||
|
||||
test('Catch error synchronous function, exit process', async () => {
|
||||
const realExit = process.exit;
|
||||
const mockExit = jest.fn();
|
||||
process.exit = mockExit;
|
||||
const fn = jest.fn(() => {
|
||||
throw new Error('Error');
|
||||
});
|
||||
const wrapped = errorBoundary(fn);
|
||||
await wrapped();
|
||||
expect(fn).toHaveBeenCalled();
|
||||
expect(print.error.mock.calls).toEqual([['Error']]);
|
||||
expect(mockExit).toHaveBeenCalled();
|
||||
process.exit = realExit;
|
||||
});
|
||||
|
||||
test('Catch error asynchronous function, exit process', async () => {
|
||||
const realExit = process.exit;
|
||||
const mockExit = jest.fn();
|
||||
process.exit = mockExit;
|
||||
const fn = jest.fn(async () => {
|
||||
await wait(3);
|
||||
throw new Error('Async Error');
|
||||
});
|
||||
const wrapped = errorBoundary(fn);
|
||||
await wrapped();
|
||||
expect(fn).toHaveBeenCalled();
|
||||
expect(print.error.mock.calls).toEqual([['Async Error']]);
|
||||
expect(mockExit).toHaveBeenCalled();
|
||||
process.exit = realExit;
|
||||
});
|
55
packages/cli/src/utils/getLowdefyVersion.js
Normal file
55
packages/cli/src/utils/getLowdefyVersion.js
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
Copyright 2020 Lowdefy, Inc
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import { type } from '@lowdefy/helpers';
|
||||
import { readFile } from '@lowdefy/node-utils';
|
||||
import YAML from 'js-yaml';
|
||||
|
||||
async function getLowdefyVersion(baseDirectory) {
|
||||
const lowdefyYaml = await readFile(path.resolve(baseDirectory || process.cwd(), 'lowdefy.yaml'));
|
||||
if (!lowdefyYaml) {
|
||||
if (baseDirectory) {
|
||||
throw new Error(
|
||||
`Could not find "lowdefy.yaml" file in specified base directory ${baseDirectory}.`
|
||||
);
|
||||
}
|
||||
throw new Error(
|
||||
`Could not find "lowdefy.yaml" file in current working directory. Change directory to a Lowdefy project, or specify a base directory.`
|
||||
);
|
||||
}
|
||||
let lowdefy;
|
||||
try {
|
||||
lowdefy = YAML.safeLoad(lowdefyYaml);
|
||||
} catch (error) {
|
||||
throw new Error(`Could not parse "lowdefy.yaml" file. Received error ${error.message}.`);
|
||||
}
|
||||
if (!lowdefy.version) {
|
||||
throw new Error(
|
||||
`No version specified in "lowdefy.yaml" file. Specify a version in the "version field".`
|
||||
);
|
||||
}
|
||||
if (!type.isString(lowdefy.version) || !lowdefy.version.match(/\d+\.\d+\.\d+(-\w+\.\d+)?/)) {
|
||||
throw new Error(
|
||||
`Version number specified in "lowdefy.yaml" file is not valid. Received ${JSON.stringify(
|
||||
lowdefy.version
|
||||
)}.`
|
||||
);
|
||||
}
|
||||
return lowdefy.version;
|
||||
}
|
||||
|
||||
export default getLowdefyVersion;
|
128
packages/cli/src/utils/getLowdefyVersion.test.js
Normal file
128
packages/cli/src/utils/getLowdefyVersion.test.js
Normal file
@ -0,0 +1,128 @@
|
||||
import path from 'path';
|
||||
import { readFile } from '@lowdefy/node-utils';
|
||||
import getLowdefyVersion from './getLowdefyVersion';
|
||||
|
||||
jest.mock('@lowdefy/node-utils', () => {
|
||||
const readFile = jest.fn();
|
||||
return {
|
||||
readFile,
|
||||
};
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
readFile.mockReset();
|
||||
});
|
||||
|
||||
test('get version from yaml file', async () => {
|
||||
readFile.mockImplementation((filePath) => {
|
||||
if (filePath === path.resolve(process.cwd(), 'lowdefy.yaml')) {
|
||||
return `
|
||||
version: 1.0.0
|
||||
`;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
const version = await getLowdefyVersion();
|
||||
expect(version).toEqual('1.0.0');
|
||||
});
|
||||
|
||||
test('get version from yaml file, base dir specified', async () => {
|
||||
readFile.mockImplementation((filePath) => {
|
||||
if (filePath === path.resolve(process.cwd(), 'baseDir/lowdefy.yaml')) {
|
||||
return `
|
||||
version: 1.0.0
|
||||
`;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
const version = await getLowdefyVersion('./baseDir');
|
||||
expect(version).toEqual('1.0.0');
|
||||
});
|
||||
|
||||
test('could not find lowdefy.yaml in cwd', async () => {
|
||||
readFile.mockImplementation((filePath) => {
|
||||
if (filePath === path.resolve(process.cwd(), 'lowdefy.yaml')) {
|
||||
return null;
|
||||
}
|
||||
return `
|
||||
version: 1.0.0
|
||||
`;
|
||||
});
|
||||
await expect(getLowdefyVersion()).rejects.toThrow(
|
||||
'Could not find "lowdefy.yaml" file in current working directory. Change directory to a Lowdefy project, or specify a base directory.'
|
||||
);
|
||||
});
|
||||
|
||||
test('could not find lowdefy.yaml in base dir', async () => {
|
||||
readFile.mockImplementation((filePath) => {
|
||||
if (filePath === path.resolve(process.cwd(), 'baseDir/lowdefy.yaml')) {
|
||||
return null;
|
||||
}
|
||||
return `
|
||||
version: 1.0.0
|
||||
`;
|
||||
});
|
||||
await expect(getLowdefyVersion('./baseDir')).rejects.toThrow(
|
||||
'Could not find "lowdefy.yaml" file in specified base directory'
|
||||
);
|
||||
});
|
||||
|
||||
test('lowdefy.yaml is invalid yaml', async () => {
|
||||
readFile.mockImplementation((filePath) => {
|
||||
if (filePath === path.resolve(process.cwd(), 'lowdefy.yaml')) {
|
||||
return `
|
||||
version: 1.0.0
|
||||
- a: a
|
||||
b: b
|
||||
`;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
await expect(getLowdefyVersion()).rejects.toThrow(
|
||||
'Could not parse "lowdefy.yaml" file. Received error '
|
||||
);
|
||||
});
|
||||
|
||||
test('No version specified', async () => {
|
||||
readFile.mockImplementation((filePath) => {
|
||||
if (filePath === path.resolve(process.cwd(), 'lowdefy.yaml')) {
|
||||
return `
|
||||
pages:
|
||||
- id: page1
|
||||
type: Context
|
||||
`;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
await expect(getLowdefyVersion()).rejects.toThrow(
|
||||
'No version specified in "lowdefy.yaml" file. Specify a version in the "version field".'
|
||||
);
|
||||
});
|
||||
|
||||
test('Version is not a string', async () => {
|
||||
readFile.mockImplementation((filePath) => {
|
||||
if (filePath === path.resolve(process.cwd(), 'lowdefy.yaml')) {
|
||||
return `
|
||||
version: 1
|
||||
`;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
await expect(getLowdefyVersion()).rejects.toThrow(
|
||||
'Version number specified in "lowdefy.yaml" file is not valid. Received 1.'
|
||||
);
|
||||
});
|
||||
|
||||
test('Version is not a valid version number', async () => {
|
||||
readFile.mockImplementation((filePath) => {
|
||||
if (filePath === path.resolve(process.cwd(), 'lowdefy.yaml')) {
|
||||
return `
|
||||
version: v1-0-3
|
||||
`;
|
||||
}
|
||||
return null;
|
||||
});
|
||||
await expect(getLowdefyVersion()).rejects.toThrow(
|
||||
'Version number specified in "lowdefy.yaml" file is not valid. Received "v1-0-3".'
|
||||
);
|
||||
});
|
Loading…
Reference in New Issue
Block a user