Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support environments api #7045

Open
wants to merge 2 commits into
base: release/4.0
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 32 additions & 19 deletions packages/ice/src/bundler/rspack/getConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { getFallbackEntry, getReCompilePlugin, getServerPlugin, getSpinnerPlugin
import { getExpandedEnvs } from '../../utils/runtimeEnv.js';
import type { BundlerOptions, Context } from '../types.js';
import type { PluginData } from '../../types/plugin.js';
import { bundlerConfigContext } from '../../service/onGetBundlerConfig.js';

type GetConfig = (
context: Context,
Expand Down Expand Up @@ -71,25 +72,37 @@ const getConfig: GetConfig = async (context, options, rspack) => {
getReCompilePlugin(reCompile, routeManifest),
].filter(Boolean) as Config['plugins'];
};
return await Promise.all(taskConfigs.map(async ({ config }) => {
const plugins = getPlugins(config);
return await getRspackConfig({
rootDir,
rspack,
runtimeTmpDir: RUNTIME_TMP_DIR,
runtimeDefineVars: {
[IMPORT_META_TARGET]: JSON.stringify(config.target),
[IMPORT_META_RENDERER]: JSON.stringify('client'),
},
getRoutesFile,
getExpandedEnvs,
localIdentName: config.cssModules?.localIdentName || (config.mode === 'development' ? CSS_MODULES_LOCAL_IDENT_NAME_DEV : CSS_MODULES_LOCAL_IDENT_NAME),
taskConfig: {
...config,
plugins: (config.plugins || []).concat(plugins),
},
});
}));
return await Promise.all(
taskConfigs.map(async ({ config, name }) => {
const plugins = getPlugins(config);
const rspackConfig = await getRspackConfig({
rootDir,
rspack,
runtimeTmpDir: RUNTIME_TMP_DIR,
runtimeDefineVars: {
[IMPORT_META_TARGET]: JSON.stringify(config.target),
[IMPORT_META_RENDERER]: JSON.stringify('client'),
},
getRoutesFile,
getExpandedEnvs,
localIdentName:
config.cssModules?.localIdentName ||
(config.mode === 'development' ? CSS_MODULES_LOCAL_IDENT_NAME_DEV : CSS_MODULES_LOCAL_IDENT_NAME),
taskConfig: {
...config,
plugins: (config.plugins || []).concat(plugins),
},
});

// run onGetBundlerConfig hooks
const finalConfig = await bundlerConfigContext.runOnGetBundlerConfig(rspackConfig, {
environment: { name },
type: 'rspack',
});

return finalConfig as Configuration;
}),
);
};

type GetDataLoaderRspackConfig = (
Expand Down
20 changes: 17 additions & 3 deletions packages/ice/src/bundler/webpack/getWebpackConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type RouteManifest from '../../utils/routeManifest.js';
import type ServerRunnerPlugin from '../../webpack/ServerRunnerPlugin.js';
import type ServerCompilerPlugin from '../../webpack/ServerCompilerPlugin.js';
import type { BundlerOptions, Context } from '../types.js';
import { bundlerConfigContext } from '../../service/onGetBundlerConfig.js';

const { debounce } = lodash;

Expand Down Expand Up @@ -67,7 +68,7 @@ const getWebpackConfig: GetWebpackConfig = async (context, options) => {
const { target = WEB } = commandArgs;
const userConfigHash = await getFileHash(configFilePath);

const webpackConfigs = taskConfigs.map(({ config }) => {
const webpackConfigs = taskConfigs.map(({ config, name }) => {
const { useDevServer, useDataLoader, server } = config;
// If the target in the task config doesn't exit, use the target from cli command option.
config.target ||= target;
Expand Down Expand Up @@ -125,10 +126,23 @@ const getWebpackConfig: GetWebpackConfig = async (context, options) => {
// Add spinner for webpack task.
webpackConfig.plugins.push(getSpinnerPlugin(spinner));

return webpackConfig;
return {
webpackConfig,
name,
};
});

return webpackConfigs;
const finalConfigs = await Promise.all(
webpackConfigs.map(async ({ webpackConfig, name }) => {
const result = await bundlerConfigContext.runOnGetBundlerConfig(webpackConfig, {
environment: { name },
type: 'webpack',
});
return result;
}),
);

return finalConfigs as Configuration[];
};

export default getWebpackConfig;
4 changes: 4 additions & 0 deletions packages/ice/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,10 @@ const userConfig = [
validation: 'boolean',
defaultValue: true,
},
{
name: 'environments',
validation: 'object',
},
];

const cliOption = [
Expand Down
14 changes: 14 additions & 0 deletions packages/ice/src/createService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ import rspackBundler from './bundler/rspack/index.js';
import getDefaultTaskConfig from './plugins/task.js';
import { multipleServerEntry, renderMultiEntry } from './utils/multipleEntry.js';
import hasDocument from './utils/hasDocument.js';
import { onGetBundlerConfig } from './service/onGetBundlerConfig.js';
import { onGetEnvironmentConfig, environmentConfigContext } from './service/onGetEnvironmentConfig.js';

const require = createRequire(import.meta.url);
const __dirname = path.dirname(fileURLToPath(import.meta.url));
Expand Down Expand Up @@ -186,6 +188,8 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
const defaultTaskConfig = getDefaultTaskConfig({ rootDir, command });
return ctx.registerTask(target, mergeConfig(defaultTaskConfig, config));
},
onGetBundlerConfig,
onGetEnvironmentConfig,
},
});
// Load .env before resolve user config, so we can access env variables defined in .env files.
Expand Down Expand Up @@ -384,6 +388,16 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt
dataLoader: command !== 'build' || loaderExports,
});

const { environments } = userConfig;
if (environments) {
for (const [envName, envConfig] of Object.entries(environments)) {
const envTaskConfig = mergeConfig(Object.assign({}, platformTaskConfig.config), envConfig as Config);
ctx.registerTask(envName, envTaskConfig);
}

environmentConfigContext.runOnGetEnvironmentConfig(taskConfigs);
}

return {
run: async () => {
const bundlerConfig = {
Expand Down
41 changes: 41 additions & 0 deletions packages/ice/src/service/onGetBundlerConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { Configuration as RspackConfiguration } from '@rspack/core';
import type { Configuration as WebpackConfiguration } from 'webpack';
import type { EnvironmentContext } from '../types';

type BundlerType = 'rspack' | 'webpack';
type BundlerConfig = RspackConfiguration | WebpackConfiguration;

export interface ModifyBundlerConfigOption {
environment: EnvironmentContext;
type: BundlerType;
}

type ModifyConfigFn = (
config: BundlerConfig,
options: ModifyBundlerConfigOption,
) => Promise<BundlerConfig | void> | void | BundlerConfig;

export type OnGetBundlerConfig = (cb: ModifyConfigFn) => void;

class BundlerConfigContext {
private modifyConfigFns: ModifyConfigFn[] = [];

onGetBundlerConfig(cb: ModifyConfigFn) {
this.modifyConfigFns.push(cb);
}

async runOnGetBundlerConfig(config: BundlerConfig, options: ModifyBundlerConfigOption) {
for (const fn of this.modifyConfigFns) {
const result = await fn(config, options);
if (result) {
config = result;
}
}
return config;
}
}

export const bundlerConfigContext = new BundlerConfigContext();

export const onGetBundlerConfig: OnGetBundlerConfig =
bundlerConfigContext.onGetBundlerConfig.bind(bundlerConfigContext);
36 changes: 36 additions & 0 deletions packages/ice/src/service/onGetEnvironmentConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import type { TaskConfig } from 'build-scripts';
import type { Config, EnvironmentContext } from '../types';

interface ModifyEnvironmentConfigOption {
environment: EnvironmentContext;
}

export type OnGetEnvironmentConfig = (cb: ModifyEnvironmentConfigFn) => void;

type ModifyEnvironmentConfigFn = (
config: Config,
options: ModifyEnvironmentConfigOption,
) => Promise<Config | void> | void | Config;

class EnvironmentConfigContext {
private modifyConfigFns: ModifyEnvironmentConfigFn[] = [];

onGetEnvironmentConfig(cb: ModifyEnvironmentConfigFn) {
this.modifyConfigFns.push(cb);
}

async runOnGetEnvironmentConfig(taskConfigs: TaskConfig<Config>[]) {
for (const fn of this.modifyConfigFns) {
taskConfigs.forEach(async (taskConfig) => {
const result = await fn(taskConfig.config, { environment: { name: taskConfig.name } });
if (result) {
taskConfig.config = result;
}
});
}
}
}

export const environmentConfigContext = new EnvironmentConfigContext();
export const onGetEnvironmentConfig: OnGetEnvironmentConfig =
environmentConfigContext.onGetEnvironmentConfig.bind(environmentConfigContext);
23 changes: 23 additions & 0 deletions packages/ice/src/types/environment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { UserConfig } from './userConfig';

type EnvironmentNotSupportConfig = Omit<
UserConfig,
| 'server'
| 'configureWebpack'
| 'webpack'
| 'routes'
| 'eslint'
| 'tsChecker'
| 'ssr'
| 'ssg'
| 'optimization'
| 'mock'
| 'plugins'
>;

export type EnvironmentUserConfig = Record<string, EnvironmentNotSupportConfig>;

export interface EnvironmentContext {
name: string;
// dependencies: Set<string>;
}
1 change: 1 addition & 0 deletions packages/ice/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from './plugin.js';
export * from './userConfig.js';
// Export type webpack for same instance of webpack.
export type { Config, webpack } from '@ice/shared-config/types';
export * from './environment.js';
4 changes: 4 additions & 0 deletions packages/ice/src/types/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import type { Config } from '@ice/shared-config/types';
import type { AppConfig, AssetsManifest } from '@ice/runtime/types';
import type ServerCompileTask from '../utils/ServerCompileTask.js';
import type { CreateLogger } from '../utils/logger.js';
import type { OnGetEnvironmentConfig } from '../service/onGetEnvironmentConfig.js';
import type { OnGetBundlerConfig } from '../service/onGetBundlerConfig.js';
import type { DeclarationData, AddRenderFile, AddTemplateFiles, ModifyRenderData, AddDataLoaderImport, Render } from './generator.js';

export type { CreateLoggerReturnType } from '../utils/logger.js';
Expand Down Expand Up @@ -167,6 +169,8 @@ export interface ExtendsPluginAPI {
addRoutesDefinition: (defineRoutes: DefineExtraRoutes) => void;
dataCache: Map<string, string>;
createLogger: CreateLogger;
onGetBundlerConfig: OnGetBundlerConfig;
onGetEnvironmentConfig: OnGetEnvironmentConfig;
}

export interface OverwritePluginAPI extends ExtendsPluginAPI {
Expand Down
5 changes: 5 additions & 0 deletions packages/ice/src/types/userConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import type { UnpluginOptions } from '@ice/bundles/compiled/unplugin/index.js';
import type { ProcessOptions } from '@ice/bundles';
import type { Config, ModifyWebpackConfig, MinimizerOptions } from '@ice/shared-config/types';
import type { OverwritePluginAPI } from './plugin';
import type { EnvironmentUserConfig } from './environment';

interface SyntaxFeatures {
// syntax exportDefaultFrom and functionBind is not supported by esbuild
Expand Down Expand Up @@ -281,4 +282,8 @@ export interface UserConfig {
* @see https://v3.ice.work/docs/guide/basic/config#crossoriginloading
*/
crossOriginLoading?: Config['output']['crossOriginLoading'];
/**
* Setup environments
*/
environments?: EnvironmentUserConfig;
}
6 changes: 5 additions & 1 deletion tests/utils/browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,11 @@ export default class Browser {
}

async start() {
this.browser = await puppeteer.launch();
this.browser = await puppeteer.launch(
{
args: ['--no-sandbox', '--disable-setuid-sandbox'],
},
);
}

async close() {
Expand Down
Loading