mirror of
https://github.com/lowdefy/lowdefy.git
synced 2025-02-17 14:30:34 +08:00
feat(cli): improve module federation, use @lowdefy/graphql-federated
This commit is contained in:
parent
649db97233
commit
1139232308
@ -15,13 +15,18 @@
|
||||
*/
|
||||
|
||||
import createContext from '../../utils/context';
|
||||
import getBuildScript from '../../utils/getBuildScript';
|
||||
import getFederatedModule from '../../utils/getFederatedModule';
|
||||
|
||||
async function build(options) {
|
||||
const context = await createContext(options);
|
||||
await getBuildScript(context);
|
||||
const { default: buildScript } = await getFederatedModule({
|
||||
module: 'build',
|
||||
packageName: '@lowdefy/build',
|
||||
version: context.version,
|
||||
context,
|
||||
});
|
||||
context.print.info('Starting build.');
|
||||
await context.buildScript({
|
||||
await buildScript({
|
||||
logger: context.print,
|
||||
cacheDirectory: context.cacheDirectory,
|
||||
configDirectory: context.baseDirectory,
|
||||
|
@ -19,7 +19,7 @@ import { spawnSync } from 'child_process';
|
||||
|
||||
import checkChildProcessError from '../../utils/checkChildProcessError';
|
||||
import createContext from '../../utils/context';
|
||||
import getBuildScript from '../../utils/getBuildScript';
|
||||
import getFederatedModule from '../../utils/getFederatedModule';
|
||||
import fetchNpmTarball from '../../utils/fetchNpmTarball';
|
||||
|
||||
async function buildNetlify(options) {
|
||||
@ -33,7 +33,7 @@ async function buildNetlify(options) {
|
||||
|
||||
context.print.spin('Fetching Lowdefy Netlify server.');
|
||||
await fetchNpmTarball({
|
||||
name: '@lowdefy/server-netlify',
|
||||
packageName: '@lowdefy/server-netlify',
|
||||
version: context.version,
|
||||
directory: netlifyDir,
|
||||
});
|
||||
@ -49,15 +49,21 @@ async function buildNetlify(options) {
|
||||
message: 'Failed to npm install Netlify server.',
|
||||
});
|
||||
|
||||
context.print.log('npm install successful.');
|
||||
context.print.log(proccessOutput.stdout.toString('utf8'));
|
||||
|
||||
context.print.spin('Fetching Lowdefy build script.');
|
||||
await getBuildScript(context);
|
||||
const { default: buildScript } = await getFederatedModule({
|
||||
module: 'build',
|
||||
packageName: '@lowdefy/build',
|
||||
version: context.version,
|
||||
context,
|
||||
});
|
||||
context.print.log('Fetched Lowdefy build script.');
|
||||
|
||||
context.print.spin('Starting Lowdefy build.');
|
||||
const outputDirectory = path.resolve(netlifyDir, './package/dist/functions/graphql/build');
|
||||
await context.buildScript({
|
||||
await buildScript({
|
||||
logger: context.print,
|
||||
cacheDirectory: context.cacheDirectory,
|
||||
configDirectory: context.baseDirectory,
|
||||
|
@ -24,16 +24,26 @@ import { createGetSecretsFromEnv } from '@lowdefy/node-utils';
|
||||
|
||||
import BatchChanges from '../../utils/BatchChanges';
|
||||
import createContext from '../../utils/context';
|
||||
import getBuildScript from '../../utils/getBuildScript';
|
||||
import getGraphql from './getGraphql';
|
||||
import getFederatedModule from '../../utils/getFederatedModule';
|
||||
import { outputDirectoryPath } from '../../utils/directories';
|
||||
|
||||
async function dev(options) {
|
||||
// Setup
|
||||
if (!options.port) options.port = 3000;
|
||||
const context = await createContext(options);
|
||||
await getBuildScript(context);
|
||||
await getGraphql(context);
|
||||
const { default: buildScript } = await getFederatedModule({
|
||||
module: 'build',
|
||||
packageName: '@lowdefy/build',
|
||||
version: context.version,
|
||||
context,
|
||||
});
|
||||
|
||||
const { typeDefs, resolvers, createContext: createGqlContext } = await getFederatedModule({
|
||||
module: 'graphql',
|
||||
packageName: '@lowdefy/graphql-federated',
|
||||
version: context.version,
|
||||
context,
|
||||
});
|
||||
|
||||
context.print.log('Starting Lowdefy development server.');
|
||||
|
||||
@ -43,7 +53,6 @@ async function dev(options) {
|
||||
logger: console,
|
||||
getSecrets: createGetSecretsFromEnv(),
|
||||
};
|
||||
const { typeDefs, resolvers, createContext: createGqlContext } = context.graphql;
|
||||
const gqlContext = createGqlContext(config);
|
||||
const server = new ApolloServer({ typeDefs, resolvers, context: gqlContext });
|
||||
|
||||
@ -63,7 +72,7 @@ async function dev(options) {
|
||||
// File watcher
|
||||
const fn = async () => {
|
||||
context.print.log('Building configuration.');
|
||||
await context.buildScript({
|
||||
await buildScript({
|
||||
logger: context.print,
|
||||
cacheDirectory: context.cacheDirectory,
|
||||
configDirectory: context.baseDirectory,
|
||||
|
@ -1,41 +0,0 @@
|
||||
/*
|
||||
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 fs from 'fs';
|
||||
import path from 'path';
|
||||
import fetchNpmTarball from '../../utils/fetchNpmTarball';
|
||||
import loadModule from '../../utils/loadModule';
|
||||
|
||||
async function getGraphql(context) {
|
||||
const cleanVersion = context.version.replace(/[-.]/g, '_');
|
||||
const cachePath = path.resolve(context.cacheDirectory, `scripts/graphql_${cleanVersion}`);
|
||||
if (!fs.existsSync(path.resolve(cachePath, 'package/dist/remoteEntry.js'))) {
|
||||
context.print.spin(`Fetching @lowdefy/graphql@${context.version} to cache.`);
|
||||
await fetchNpmTarball({
|
||||
name: '@lowdefy/graphql',
|
||||
version: context.version,
|
||||
directory: cachePath,
|
||||
});
|
||||
context.print.log(`Fetched @lowdefy/build@${context.version} to cache.`);
|
||||
}
|
||||
context.graphql = await loadModule(
|
||||
path.resolve(cachePath, 'package/dist/moduleFederation'),
|
||||
'./graphql'
|
||||
);
|
||||
return context;
|
||||
}
|
||||
|
||||
export default getGraphql;
|
@ -18,24 +18,25 @@ import axios from 'axios';
|
||||
import decompress from 'decompress';
|
||||
import decompressTargz from 'decompress-targz';
|
||||
|
||||
async function fetchNpmTarball({ name, version, directory }) {
|
||||
const registryUrl = `https://registry.npmjs.org/${name}`;
|
||||
async function fetchNpmTarball({ packageName, version, directory }) {
|
||||
const registryUrl = `https://registry.npmjs.org/${packageName}`;
|
||||
let packageInfo;
|
||||
try {
|
||||
packageInfo = await axios.get(registryUrl);
|
||||
} catch (error) {
|
||||
if (error.response && error.response.status === 404) {
|
||||
throw new Error(`Package "${name}" could not be found at ${registryUrl}.`);
|
||||
console.log('404');
|
||||
throw new Error(`Package "${packageName}" could not be found at ${registryUrl}.`);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
||||
if (!packageInfo || !packageInfo.data) {
|
||||
throw new Error(`Package "${name}" could not be found at ${registryUrl}.`);
|
||||
throw new Error(`Package "${packageName}" could not be found at ${registryUrl}.`);
|
||||
}
|
||||
|
||||
if (!packageInfo.data.versions[version]) {
|
||||
throw new Error(`Invalid version. "${name}" does not have version "${version}".`);
|
||||
throw new Error(`Invalid version. "${packageName}" does not have version "${version}".`);
|
||||
}
|
||||
let tarball;
|
||||
|
||||
@ -46,7 +47,7 @@ async function fetchNpmTarball({ name, version, directory }) {
|
||||
} catch (error) {
|
||||
if (error.response && error.response.status === 404) {
|
||||
throw new Error(
|
||||
`Package "${name}" tarball could not be found at ${packageInfo.data.versions[version].dist.tarball}.`
|
||||
`Package "${packageName}" tarball could not be found at ${packageInfo.data.versions[version].dist.tarball}.`
|
||||
);
|
||||
}
|
||||
throw error;
|
||||
@ -54,7 +55,7 @@ async function fetchNpmTarball({ name, version, directory }) {
|
||||
|
||||
if (!tarball || !tarball.data) {
|
||||
throw new Error(
|
||||
`Package "${name}" tarball could not be found at ${packageInfo.data.versions[version].dist.tarball}.`
|
||||
`Package "${packageName}" tarball could not be found at ${packageInfo.data.versions[version].dist.tarball}.`
|
||||
);
|
||||
}
|
||||
await decompress(tarball.data, directory, {
|
||||
|
@ -89,43 +89,45 @@ jest.mock('axios', () => {
|
||||
});
|
||||
|
||||
test('valid package and version', async () => {
|
||||
await fetchNpmTarball({ name: 'valid-package', version: '1.0.0', directory });
|
||||
await fetchNpmTarball({ packageName: 'valid-package', version: '1.0.0', directory });
|
||||
expect(true).toBe(true);
|
||||
});
|
||||
|
||||
test('version does not exist', async () => {
|
||||
await expect(
|
||||
fetchNpmTarball({ name: 'valid-package', version: 'invalid', directory })
|
||||
fetchNpmTarball({ packageName: 'valid-package', version: 'invalid', directory })
|
||||
).rejects.toThrow('Invalid version. "valid-package" does not have version "invalid"');
|
||||
});
|
||||
|
||||
test('npm return a 404', async () => {
|
||||
await expect(fetchNpmTarball({ name: '404', version: '1.0.0', directory })).rejects.toThrow(
|
||||
'Package "404" could not be found at https://registry.npmjs.org/404.'
|
||||
);
|
||||
await expect(
|
||||
fetchNpmTarball({ packageName: '404', version: '1.0.0', directory })
|
||||
).rejects.toThrow('Package "404" could not be found at https://registry.npmjs.org/404.');
|
||||
});
|
||||
|
||||
test('axios error', async () => {
|
||||
await expect(
|
||||
fetchNpmTarball({ name: 'axios-error', version: '1.0.0', directory })
|
||||
fetchNpmTarball({ packageName: 'axios-error', version: '1.0.0', directory })
|
||||
).rejects.toThrow('Axios error');
|
||||
});
|
||||
|
||||
test('empty response', async () => {
|
||||
await expect(fetchNpmTarball({ name: 'no-data', version: '1.0.0', directory })).rejects.toThrow(
|
||||
'Package "no-data" could not be found at https://registry.npmjs.org/no-data.'
|
||||
);
|
||||
await expect(
|
||||
fetchNpmTarball({ packageName: 'no-data', version: '1.0.0', directory })
|
||||
).rejects.toThrow('Package "no-data" could not be found at https://registry.npmjs.org/no-data.');
|
||||
});
|
||||
|
||||
test('undefined response', async () => {
|
||||
await expect(fetchNpmTarball({ name: 'undefined', version: '1.0.0', directory })).rejects.toThrow(
|
||||
await expect(
|
||||
fetchNpmTarball({ packageName: 'undefined', version: '1.0.0', directory })
|
||||
).rejects.toThrow(
|
||||
'Package "undefined" could not be found at https://registry.npmjs.org/undefined.'
|
||||
);
|
||||
});
|
||||
|
||||
test('tarball 404', async () => {
|
||||
await expect(
|
||||
fetchNpmTarball({ name: 'valid-package', version: 'v404', directory })
|
||||
fetchNpmTarball({ packageName: 'valid-package', version: 'v404', directory })
|
||||
).rejects.toThrow(
|
||||
'Package "valid-package" tarball could not be found at https://registry.npmjs.org/404.'
|
||||
);
|
||||
@ -133,13 +135,13 @@ test('tarball 404', async () => {
|
||||
|
||||
test('tarball axios error', async () => {
|
||||
await expect(
|
||||
fetchNpmTarball({ name: 'valid-package', version: 'error', directory })
|
||||
fetchNpmTarball({ packageName: 'valid-package', version: 'error', directory })
|
||||
).rejects.toThrow('Axios error');
|
||||
});
|
||||
|
||||
test('tarball empty response', async () => {
|
||||
await expect(
|
||||
fetchNpmTarball({ name: 'valid-package', version: 'noData', directory })
|
||||
fetchNpmTarball({ packageName: 'valid-package', version: 'noData', directory })
|
||||
).rejects.toThrow(
|
||||
'Package "valid-package" tarball could not be found at https://registry.npmjs.org/no-data.'
|
||||
);
|
||||
@ -147,7 +149,7 @@ test('tarball empty response', async () => {
|
||||
|
||||
test('tarball undefined response', async () => {
|
||||
await expect(
|
||||
fetchNpmTarball({ name: 'valid-package', version: 'undef', directory })
|
||||
fetchNpmTarball({ packageName: 'valid-package', version: 'undef', directory })
|
||||
).rejects.toThrow(
|
||||
'Package "valid-package" tarball could not be found at https://registry.npmjs.org/undefined.'
|
||||
);
|
||||
|
@ -19,21 +19,23 @@ import path from 'path';
|
||||
import fetchNpmTarball from './fetchNpmTarball';
|
||||
import loadModule from './loadModule';
|
||||
|
||||
async function getBuildScript(context) {
|
||||
const cleanVersion = context.version.replace(/[-.]/g, '_');
|
||||
const cachePath = path.resolve(context.cacheDirectory, `scripts/build_${cleanVersion}`);
|
||||
async function getFederatedModule({ module, packageName, version, context }) {
|
||||
const cleanVersion = version.replace(/[-.]/g, '_');
|
||||
const cachePath = path.resolve(context.cacheDirectory, `scripts/${module}/${cleanVersion}`);
|
||||
if (!fs.existsSync(path.resolve(cachePath, 'package/dist/remoteEntry.js'))) {
|
||||
context.print.spin(`Fetching @lowdefy/build@${context.version} to cache.`);
|
||||
context.print.spin(`Fetching ${packageName}@${version} to cache.`);
|
||||
console.log('start fetch tarball');
|
||||
await fetchNpmTarball({
|
||||
name: '@lowdefy/build',
|
||||
version: context.version,
|
||||
packageName,
|
||||
version,
|
||||
directory: cachePath,
|
||||
});
|
||||
context.print.log(`Fetched @lowdefy/build@${context.version} to cache.`);
|
||||
context.print.log(`Fetched ${packageName}@${version} to cache.`);
|
||||
}
|
||||
const buildScript = await loadModule(path.resolve(cachePath, 'package/dist'), './build');
|
||||
context.buildScript = buildScript.default;
|
||||
return context;
|
||||
return loadModule({
|
||||
directory: path.resolve(cachePath, 'package/dist'),
|
||||
module: `./${module}`,
|
||||
});
|
||||
}
|
||||
|
||||
export default getBuildScript;
|
||||
export default getFederatedModule;
|
@ -18,7 +18,7 @@
|
||||
|
||||
import path from 'path';
|
||||
|
||||
async function loadModule(dir, moduleName, remoteEntry = 'remoteEntry.js') {
|
||||
async function loadModule({ directory, module, remoteEntry = 'remoteEntry.js' }) {
|
||||
const importRemote = async (remoteEntryFile) => {
|
||||
if (__webpack_share_scopes__.default) {
|
||||
await __webpack_init_sharing__('default');
|
||||
@ -39,9 +39,9 @@ async function loadModule(dir, moduleName, remoteEntry = 'remoteEntry.js') {
|
||||
});
|
||||
};
|
||||
|
||||
const container = await importRemote(path.resolve(`${dir}/${remoteEntry}`));
|
||||
const container = await importRemote(path.resolve(`${directory}/${remoteEntry}`));
|
||||
|
||||
return container.get(moduleName).then((factory) => factory());
|
||||
return container.get(module).then((factory) => factory());
|
||||
}
|
||||
|
||||
export default loadModule;
|
||||
|
@ -53,9 +53,17 @@ function createBasicPrint() {
|
||||
};
|
||||
}
|
||||
|
||||
// Memoise print so that error handler can get the same spinner object
|
||||
let print;
|
||||
|
||||
function createPrint({ basic } = {}) {
|
||||
if (basic) return createBasicPrint();
|
||||
return createOraPrint();
|
||||
if (print) return print;
|
||||
if (basic) {
|
||||
print = createBasicPrint();
|
||||
return print;
|
||||
}
|
||||
print = createOraPrint();
|
||||
return print;
|
||||
}
|
||||
|
||||
export default createPrint;
|
||||
|
Loading…
Reference in New Issue
Block a user