mirror of
https://github.com/lowdefy/lowdefy.git
synced 2025-02-11 14:20:07 +08:00
feat: Fetch, install and build @lowdefy/server from CLI.
This commit is contained in:
parent
c97a8fa6b5
commit
7966538468
@ -14,33 +14,17 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import fse from 'fs-extra';
|
||||
import getFederatedModule from '../../utils/getFederatedModule';
|
||||
import getServer from './getServer.js';
|
||||
import installServer from './installServer.js';
|
||||
import runLowdefyBuild from './runLowdefyBuild.js';
|
||||
|
||||
async function build({ context }) {
|
||||
const { default: buildScript } = await getFederatedModule({
|
||||
module: 'build',
|
||||
packageName: '@lowdefy/build',
|
||||
version: context.lowdefyVersion,
|
||||
context,
|
||||
});
|
||||
context.print.log(
|
||||
`Cleaning block meta cache at "${path.resolve(context.cacheDirectory, './meta')}".`
|
||||
);
|
||||
|
||||
await fse.emptyDir(path.resolve(context.cacheDirectory, './meta'));
|
||||
context.print.info('Starting build.');
|
||||
await buildScript({
|
||||
blocksServerUrl: context.options.blocksServerUrl,
|
||||
buildDirectory: context.buildDirectory,
|
||||
cacheDirectory: context.cacheDirectory,
|
||||
configDirectory: context.baseDirectory,
|
||||
logger: context.print,
|
||||
refResolver: context.options.refResolver,
|
||||
});
|
||||
await getServer({ context });
|
||||
await installServer({ context });
|
||||
await runLowdefyBuild({ context });
|
||||
|
||||
await context.sendTelemetry();
|
||||
context.print.log(`Build artifacts saved at ${context.buildDirectory}.`);
|
||||
context.print.succeed(`Build successful.`);
|
||||
}
|
||||
|
||||
|
51
packages/cli/src/commands/build/getServer.js
Normal file
51
packages/cli/src/commands/build/getServer.js
Normal file
@ -0,0 +1,51 @@
|
||||
/*
|
||||
Copyright 2020-2021 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 fs from 'fs';
|
||||
import path from 'path';
|
||||
import { cleanDirectory, readFile } from '@lowdefy/node-utils';
|
||||
import fetchNpmTarball from '../../utils/fetchNpmTarball.js';
|
||||
|
||||
async function getServer({ context }) {
|
||||
let fetchServer = false;
|
||||
|
||||
const serverExists = fs.existsSync(path.join(context.directories.server, 'package.json'));
|
||||
if (!serverExists) fetchServer = true;
|
||||
|
||||
if (serverExists) {
|
||||
const serverPackageConfig = JSON.parse(
|
||||
await readFile(path.join(context.directories.server, 'package.json'))
|
||||
);
|
||||
if (serverPackageConfig.version !== context.lowdefyVersion) {
|
||||
fetchServer = true;
|
||||
context.print.warn(`Removing @lowdefy/server with version ${serverPackageConfig.version}`);
|
||||
await cleanDirectory(context.directories.server);
|
||||
}
|
||||
}
|
||||
|
||||
if (fetchServer) {
|
||||
context.print.spin('Fetching @lowdefy/server from npm.');
|
||||
await fetchNpmTarball({
|
||||
packageName: '@lowdefy/server',
|
||||
version: context.lowdefyVersion,
|
||||
directory: context.directories.server,
|
||||
});
|
||||
context.print.log('Fetched @lowdefy/server from npm.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export default getServer;
|
49
packages/cli/src/commands/build/installServer.js
Normal file
49
packages/cli/src/commands/build/installServer.js
Normal file
@ -0,0 +1,49 @@
|
||||
/*
|
||||
Copyright 2020-2021 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 execProcess from '../../utils/execProcess.js';
|
||||
import spawnProcess from '../../utils/spawnProcess.js';
|
||||
|
||||
// const commands = {
|
||||
// npm: 'npm install --legacy-peer-deps',
|
||||
// yarn: 'yarn install',
|
||||
// };
|
||||
|
||||
const args = {
|
||||
npm: ['install', '--legacy-peer-deps'],
|
||||
yarn: ['install'],
|
||||
};
|
||||
|
||||
async function installServer({ context }) {
|
||||
context.print.spin(`Running ${context.packageManager} install.`);
|
||||
try {
|
||||
await spawnProcess({
|
||||
context,
|
||||
command: context.packageManager, // npm or yarn
|
||||
args: args[context.packageManager],
|
||||
processOptions: {
|
||||
cwd: context.directories.server,
|
||||
},
|
||||
silent: false,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw new Error(`${context.packageManager} install failed.`);
|
||||
}
|
||||
context.print.log(`${context.packageManager} install successful.`);
|
||||
}
|
||||
|
||||
export default installServer;
|
41
packages/cli/src/commands/build/runLowdefyBuild.js
Normal file
41
packages/cli/src/commands/build/runLowdefyBuild.js
Normal file
@ -0,0 +1,41 @@
|
||||
/*
|
||||
Copyright 2020-2021 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 execProcess from '../../utils/execProcess.js';
|
||||
|
||||
const commands = {
|
||||
npm: 'npm run build:lowdefy',
|
||||
yarn: 'yarn run build:lowdefy',
|
||||
};
|
||||
|
||||
async function runLowdefyBuild({ context }) {
|
||||
context.print.log('Running Lowdefy build.');
|
||||
try {
|
||||
await execProcess({
|
||||
context,
|
||||
command: commands[context.packageManager],
|
||||
processOptions: {
|
||||
cwd: context.directories.server,
|
||||
},
|
||||
silent: false,
|
||||
});
|
||||
} catch (error) {
|
||||
throw new Error('Lowdefy build failed.');
|
||||
}
|
||||
context.print.log('Lowdefy build successful.');
|
||||
}
|
||||
|
||||
export default runLowdefyBuild;
|
@ -17,7 +17,7 @@
|
||||
import { readFile } from '@lowdefy/node-utils';
|
||||
|
||||
import program from 'commander';
|
||||
// import build from './commands/build/build.js';
|
||||
import build from './commands/build/build.js';
|
||||
// import dev from './commands/dev/dev.js';
|
||||
import init from './commands/init/init.js';
|
||||
import runCommand from './utils/runCommand.js';
|
||||
@ -29,28 +29,32 @@ const { description, version } = packageJson;
|
||||
|
||||
program.name('lowdefy').description(description).version(version, '-v, --version');
|
||||
|
||||
// program
|
||||
// .command('build')
|
||||
// .description('Build a Lowdefy deployment.')
|
||||
// .usage(`[options]`)
|
||||
// .option(
|
||||
// '--base-directory <base-directory>',
|
||||
// 'Change base directory. Default is the current working directory.'
|
||||
// )
|
||||
// .option(
|
||||
// '--blocks-server-url <blocks-server-url>',
|
||||
// 'The URL from where Lowdefy blocks will be served.'
|
||||
// )
|
||||
// .option('--disable-telemetry', 'Disable telemetry.')
|
||||
// .option(
|
||||
// '--output-directory <output-directory>',
|
||||
// 'Change the directory to which build artifacts are saved. Default is "<base-directory>/.lowdefy/build".'
|
||||
// )
|
||||
// .option(
|
||||
// '--ref-resolver <ref-resolver-function-path>',
|
||||
// 'Path to a JavaScript file containing a _ref resolver function to be used as the app default _ref resolver.'
|
||||
// )
|
||||
// .action(runCommand(build));
|
||||
program
|
||||
.command('build')
|
||||
.description('Build a Lowdefy deployment.')
|
||||
.usage(`[options]`)
|
||||
.option(
|
||||
'--base-directory <base-directory>',
|
||||
'Change base directory. Default is the current working directory.'
|
||||
)
|
||||
.option(
|
||||
'--blocks-server-url <blocks-server-url>',
|
||||
'The URL from where Lowdefy blocks will be served.'
|
||||
)
|
||||
.option('--disable-telemetry', 'Disable telemetry.')
|
||||
.option(
|
||||
'--output-directory <output-directory>',
|
||||
'Change the directory to which build artifacts are saved. Default is "<base-directory>/.lowdefy/build".'
|
||||
)
|
||||
.option(
|
||||
'--package-manager <package-manager>',
|
||||
'The package manager to user. Options are "npm" or "yarn".'
|
||||
)
|
||||
.option(
|
||||
'--ref-resolver <ref-resolver-function-path>',
|
||||
'Path to a JavaScript file containing a _ref resolver function to be used as the app default _ref resolver.'
|
||||
)
|
||||
.action(runCommand({ cliVersion: version })(build));
|
||||
|
||||
// program
|
||||
// .command('dev')
|
||||
@ -78,7 +82,7 @@ program.name('lowdefy').description(description).version(version, '-v, --version
|
||||
// '--watch-ignore <paths...>',
|
||||
// 'A list of paths to files or directories that should be ignored by the file watcher. Globs are supported.'
|
||||
// )
|
||||
// .action(runCommand(dev));
|
||||
// .action(runCommand({ cliVersion: version })(dev));
|
||||
|
||||
program
|
||||
.command('init')
|
||||
|
@ -1,50 +0,0 @@
|
||||
/*
|
||||
Copyright 2020-2021 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 checkChildProcessError from './checkChildProcessError.js';
|
||||
|
||||
const mockError = jest.fn();
|
||||
const context = {
|
||||
print: {
|
||||
error: mockError,
|
||||
},
|
||||
};
|
||||
|
||||
test('output status 0', () => {
|
||||
checkChildProcessError({ context, processOutput: { status: 0 }, message: 'Test Error Message' });
|
||||
// checkChildProcessError should not throw, expect is so that test passes
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
test('output status 1', () => {
|
||||
const processOutput = {
|
||||
status: 1,
|
||||
stderr: Buffer.from('Process error message'),
|
||||
};
|
||||
expect(() =>
|
||||
checkChildProcessError({ context, processOutput, message: 'Test Error Message' })
|
||||
).toThrow('Test Error Message');
|
||||
expect(mockError.mock.calls).toMatchInlineSnapshot(
|
||||
[['Process error message']],
|
||||
`
|
||||
Array [
|
||||
Array [
|
||||
"Process error message",
|
||||
],
|
||||
]
|
||||
`
|
||||
);
|
||||
});
|
@ -14,11 +14,25 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
function checkChildProcessError({ context, processOutput, message }) {
|
||||
if (processOutput.status === 1) {
|
||||
context.print.error(processOutput.stderr.toString('utf8'));
|
||||
throw new Error(message);
|
||||
import util from 'util';
|
||||
import { exec } from 'child_process';
|
||||
|
||||
const execPromise = util.promisify(exec);
|
||||
|
||||
async function execProcess({ context, command, processOptions, silent }) {
|
||||
const { stdout, stderr } = await execPromise(command, processOptions);
|
||||
if (!silent) {
|
||||
stderr.split('\n').forEach((line) => {
|
||||
if (line) {
|
||||
context.print.warn(line);
|
||||
}
|
||||
});
|
||||
stdout.split('\n').forEach((line) => {
|
||||
if (line) {
|
||||
context.print.log(line);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default checkChildProcessError;
|
||||
export default execProcess;
|
@ -59,6 +59,7 @@ async function fetchNpmTarball({ packageName, version, directory }) {
|
||||
}
|
||||
await decompress(tarball.data, directory, {
|
||||
plugins: [decompressTargz()],
|
||||
strip: 1, // Removes leading /package dir from the file path
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -17,13 +17,13 @@
|
||||
import path from 'path';
|
||||
|
||||
function getDirectories({ baseDirectory, options }) {
|
||||
let buildDirectory;
|
||||
let dotLowdefy;
|
||||
if (options.outputDirectory) {
|
||||
buildDirectory = path.resolve(options.outputDirectory);
|
||||
dotLowdefy = path.resolve(options.outputDirectory);
|
||||
} else {
|
||||
buildDirectory = path.resolve(baseDirectory, './.lowdefy/build');
|
||||
dotLowdefy = path.resolve(baseDirectory, '.lowdefy');
|
||||
}
|
||||
return { buildDirectory };
|
||||
return { dotLowdefy, server: path.join(dotLowdefy, 'server') };
|
||||
}
|
||||
|
||||
export default getDirectories;
|
||||
|
59
packages/cli/src/utils/getPackageManager.js
Normal file
59
packages/cli/src/utils/getPackageManager.js
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
Copyright 2020-2021 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.
|
||||
*/
|
||||
|
||||
// Source https://github.com/vercel/next.js/blob/canary/packages/create-next-app/helpers/should-use-yarn.ts
|
||||
|
||||
/*
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2021 Vercel, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
*/
|
||||
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
function getPackageManager({ options }) {
|
||||
if (options.packageManager) return options.packageManager;
|
||||
try {
|
||||
const userAgent = process.env.npm_config_user_agent;
|
||||
if (userAgent && userAgent.startsWith('yarn')) {
|
||||
return 'yarn';
|
||||
}
|
||||
execSync('yarnpkg --version', { stdio: 'ignore' });
|
||||
return 'yarn';
|
||||
} catch (e) {
|
||||
return 'npm';
|
||||
}
|
||||
}
|
||||
|
||||
export default getPackageManager;
|
58
packages/cli/src/utils/spawnProcess.js
Normal file
58
packages/cli/src/utils/spawnProcess.js
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
Copyright 2020-2021 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 { spawn } from 'child_process';
|
||||
|
||||
async function spawnProcess({ context, command, args, processOptions, silent }) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const process = spawn(command, args, processOptions);
|
||||
|
||||
process.stdout.on('data', (data) => {
|
||||
if (!silent) {
|
||||
data
|
||||
.toString('utf8')
|
||||
.split('\n')
|
||||
.forEach((line) => {
|
||||
if (line) {
|
||||
context.print.log(line);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
process.stderr.on('data', (data) => {
|
||||
if (!silent) {
|
||||
data
|
||||
.toString('utf8')
|
||||
.split('\n')
|
||||
.forEach((line) => {
|
||||
if (line) {
|
||||
context.print.warn(line);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
process.on('exit', (code) => {
|
||||
if (code !== 0) {
|
||||
reject(new Error(`${command} exited with code ${code}`));
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default spawnProcess;
|
@ -22,6 +22,7 @@ import getCliJson from './getCliJson.js';
|
||||
import getDirectories from './getDirectories.js';
|
||||
import getLowdefyYaml from './getLowdefyYaml.js';
|
||||
import getOptions from './getOptions.js';
|
||||
import getPackageManager from './getPackageManager.js';
|
||||
import getSendTelemetry from './getSendTelemetry.js';
|
||||
import createPrint from './print.js';
|
||||
|
||||
@ -39,10 +40,8 @@ async function startUp({ context, options = {}, command }) {
|
||||
context.appId = appId;
|
||||
|
||||
context.options = getOptions(context);
|
||||
|
||||
const { buildDirectory } = getDirectories(context);
|
||||
context.buildDirectory = buildDirectory;
|
||||
|
||||
context.directories = getDirectories(context);
|
||||
context.packageManager = getPackageManager(context);
|
||||
await checkForUpdatedVersions(context);
|
||||
|
||||
context.sendTelemetry = getSendTelemetry(context);
|
||||
|
@ -29,7 +29,7 @@
|
||||
"src/*"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "yarn build:lowdefy && yarn build:next",
|
||||
"build": "lowdefy-build && next build",
|
||||
"build:lowdefy": "lowdefy-build",
|
||||
"build:next": "next build",
|
||||
"dev": "next dev",
|
||||
|
Loading…
Reference in New Issue
Block a user