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

[babel] ensure TS preset runs before anything else #119107

Merged
merged 5 commits into from
Nov 29, 2021
Merged
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
85 changes: 48 additions & 37 deletions packages/kbn-babel-preset/common_preset.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,57 @@
* Side Public License, v 1.
*/

const plugins = [
require.resolve('babel-plugin-add-module-exports'),

// The class properties proposal was merged with the private fields proposal
// into the "class fields" proposal. Babel doesn't support this combined
// proposal yet, which includes private field, so this transform is
// TECHNICALLY stage 2, but for all intents and purposes it's stage 3
//
// See https://github.com/babel/proposals/issues/12 for progress
require.resolve('@babel/plugin-proposal-class-properties'),

// Optional Chaining proposal is stage 4 (https://github.com/tc39/proposal-optional-chaining)
// Need this since we are using TypeScript 3.7+
require.resolve('@babel/plugin-proposal-optional-chaining'),

// Nullish coalescing proposal is stage 4 (https://github.com/tc39/proposal-nullish-coalescing)
// Need this since we are using TypeScript 3.7+
require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'),

// Proposal is on stage 4, and included in ECMA-262 (https://github.com/tc39/proposal-export-ns-from)
// Need this since we are using TypeScript 3.8+
require.resolve('@babel/plugin-proposal-export-namespace-from'),

// Proposal is on stage 4, and included in ECMA-262 (https://github.com/tc39/proposal-export-ns-from)
// Need this since we are using TypeScript 3.9+
require.resolve('@babel/plugin-proposal-private-methods'),

// It enables the @babel/runtime so we can decrease the bundle sizes of the produced outputs
[
require.resolve('@babel/plugin-transform-runtime'),
module.exports = {
presets: [
// plugins always run before presets, but in this case we need the
// @babel/preset-typescript preset to run first so we have to move
// our explicit plugin configs to a sub-preset
{
version: '^7.12.5',
plugins: [
require.resolve('babel-plugin-add-module-exports'),

// The class properties proposal was merged with the private fields proposal
// into the "class fields" proposal. Babel doesn't support this combined
// proposal yet, which includes private field, so this transform is
// TECHNICALLY stage 2, but for all intents and purposes it's stage 3
//
// See https://github.com/babel/proposals/issues/12 for progress
require.resolve('@babel/plugin-proposal-class-properties'),

// Optional Chaining proposal is stage 4 (https://github.com/tc39/proposal-optional-chaining)
// Need this since we are using TypeScript 3.7+
require.resolve('@babel/plugin-proposal-optional-chaining'),

// Nullish coalescing proposal is stage 4 (https://github.com/tc39/proposal-nullish-coalescing)
// Need this since we are using TypeScript 3.7+
require.resolve('@babel/plugin-proposal-nullish-coalescing-operator'),

// Proposal is on stage 4, and included in ECMA-262 (https://github.com/tc39/proposal-export-ns-from)
// Need this since we are using TypeScript 3.8+
require.resolve('@babel/plugin-proposal-export-namespace-from'),

// Proposal is on stage 4, and included in ECMA-262 (https://github.com/tc39/proposal-export-ns-from)
// Need this since we are using TypeScript 3.9+
require.resolve('@babel/plugin-proposal-private-methods'),

// It enables the @babel/runtime so we can decrease the bundle sizes of the produced outputs
[
require.resolve('@babel/plugin-transform-runtime'),
{
version: '^7.12.5',
},
],
],
},
],
];

module.exports = {
presets: [
[require.resolve('@babel/preset-typescript'), { allowNamespaces: true }],
require.resolve('@babel/preset-react'),

[
require.resolve('@babel/preset-typescript'),
{
allowNamespaces: true,
allowDeclareFields: true,
},
],
],
plugins,
};
Original file line number Diff line number Diff line change
Expand Up @@ -35,14 +35,14 @@ export async function runFindBabelHelpersInEntryBundlesCli() {
}

for (const { userRequest } of module.reasons) {
if (userRequest.startsWith('@babel/runtime/')) {
if (userRequest.startsWith('@babel/runtime')) {
imports.add(userRequest);
}
}
}
}

log.success('found', imports.size, '@babel/register imports in entry bundles');
log.success('found', imports.size, '@babel/runtime* imports in entry bundles');
log.write(
Array.from(imports, (i) => `'${i}',`)
.sort()
Expand Down
2 changes: 1 addition & 1 deletion packages/kbn-optimizer/src/worker/emit_stats_plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export class EmitStatsPlugin {
(stats) => {
Fs.writeFileSync(
Path.resolve(this.bundle.outputDir, 'stats.json'),
JSON.stringify(stats.toJson())
JSON.stringify(stats.toJson(), null, 2)
);
}
);
Expand Down
125 changes: 109 additions & 16 deletions packages/kbn-storybook/src/webpack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
import { externals } from '@kbn/ui-shared-deps-src';
import { stringifyRequest } from 'loader-utils';
import { resolve } from 'path';
import { Configuration, Stats } from 'webpack';
import webpack, { Configuration, Stats } from 'webpack';
import webpackMerge from 'webpack-merge';
import { REPO_ROOT } from './lib/constants';
import { IgnoreNotFoundExportPlugin } from './ignore_not_found_export_plugin';

type Preset = string | [string, Record<string, unknown>] | Record<string, unknown>;

const stats = {
...Stats.presetToOptions('minimal'),
colors: true,
Expand All @@ -22,6 +24,46 @@ const stats = {
moduleTrace: true,
};

function isProgressPlugin(plugin: any) {
return 'handler' in plugin && plugin.showActiveModules && plugin.showModules;
}

function isHtmlPlugin(plugin: any): plugin is { options: { template: string } } {
return !!(typeof plugin.options?.template === 'string');
}

function isBabelLoaderRule(rule: webpack.RuleSetRule): rule is webpack.RuleSetRule & {
use: webpack.RuleSetLoader[];
} {
return !!(
rule.use &&
Array.isArray(rule.use) &&
rule.use.some(
(l) =>
typeof l === 'object' && typeof l.loader === 'string' && l.loader.includes('babel-loader')
)
);
}

function getPresetPath(preset: Preset) {
if (typeof preset === 'string') return preset;
if (Array.isArray(preset)) return preset[0];
return undefined;
}

function getTsPreset(preset: Preset) {
if (getPresetPath(preset)?.includes('preset-typescript')) {
if (typeof preset === 'string') return [preset, {}];
if (Array.isArray(preset)) return preset;

throw new Error('unsupported preset-typescript format');
}
}

function isDesiredPreset(preset: Preset) {
return !getPresetPath(preset)?.includes('preset-flow');
}

// Extend the Storybook Webpack config with some customizations
/* eslint-disable import/no-default-export */
export default function ({ config: storybookConfig }: { config: Configuration }) {
Expand Down Expand Up @@ -83,21 +125,72 @@ export default function ({ config: storybookConfig }: { config: Configuration })
stats,
};

// Disable the progress plugin
const progressPlugin: any = (storybookConfig.plugins || []).find((plugin: any) => {
return 'handler' in plugin && plugin.showActiveModules && plugin.showModules;
});
progressPlugin.handler = () => {};

// This is the hacky part. We find something that looks like the
// HtmlWebpackPlugin and mutate its `options.template` to point at our
// revised template.
const htmlWebpackPlugin: any = (storybookConfig.plugins || []).find((plugin: any) => {
return plugin.options && typeof plugin.options.template === 'string';
});
if (htmlWebpackPlugin) {
htmlWebpackPlugin.options.template = require.resolve('../templates/index.ejs');
const updatedModuleRules = [];
// clone and modify the module.rules config provided by storybook so that the default babel plugins run after the typescript preset
for (const originalRule of storybookConfig.module?.rules ?? []) {
const rule = { ...originalRule };
updatedModuleRules.push(rule);

if (isBabelLoaderRule(rule)) {
rule.use = [...rule.use];
const loader = (rule.use[0] = { ...rule.use[0] });
const options = (loader.options = { ...(loader.options as Record<string, any>) });

// capture the plugins defined at the root level
const plugins: string[] = options.plugins;
options.plugins = [];

// move the plugins to the top of the preset array so they will run after the typescript preset
options.presets = [
{
plugins,
},
...(options.presets as Preset[]).filter(isDesiredPreset).map((preset) => {
const tsPreset = getTsPreset(preset);
if (!tsPreset) {
return preset;
}

return [
tsPreset[0],
{
...tsPreset[1],
allowNamespaces: true,
allowDeclareFields: true,
},
];
}),
];
}
}

return webpackMerge(storybookConfig, config);
// copy and modify the webpack plugins added by storybook
const filteredStorybookPlugins = [];
for (const plugin of storybookConfig.plugins ?? []) {
// Remove the progress plugin
if (isProgressPlugin(plugin)) {
continue;
}

// This is the hacky part. We find something that looks like the
// HtmlWebpackPlugin and mutate its `options.template` to point at our
// revised template.
if (isHtmlPlugin(plugin)) {
plugin.options.template = require.resolve('../templates/index.ejs');
}

filteredStorybookPlugins.push(plugin);
}

return webpackMerge(
{
...storybookConfig,
plugins: filteredStorybookPlugins,
module: {
...storybookConfig.module,
rules: updatedModuleRules,
},
},
config
);
}
6 changes: 4 additions & 2 deletions src/plugins/console/public/services/history.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ const MAX_NUMBER_OF_HISTORY_ITEMS = 100;
export const isQuotaExceededError = (e: Error): boolean => e.name === 'QuotaExceededError';

export class History {
constructor(private readonly storage: Storage) {}
private changeEmitter: BehaviorSubject<any[]>;

private changeEmitter = new BehaviorSubject<any[]>(this.getHistory() || []);
constructor(private readonly storage: Storage) {
this.changeEmitter = new BehaviorSubject(this.getHistory() || []);
}

getHistoryKeys() {
return this.storage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ interface State {

export class DashboardViewport extends React.Component<DashboardViewportProps, State> {
static contextType = context;
public readonly context!: DashboardReactContextValue;
public declare readonly context: DashboardReactContextValue;

private controlsRoot: React.RefObject<HTMLDivElement>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ describe('rolesManagementApp', () => {
expect(docTitle.reset).not.toHaveBeenCalled();
expect(container).toMatchInlineSnapshot(`
<div>
Role Edit Page: {"action":"edit","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"fieldCache":{}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit","search":"","hash":""}}}
Role Edit Page: {"action":"edit","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"fieldCache":{},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit","search":"","hash":""}}}
</div>
`);

Expand All @@ -129,7 +129,7 @@ describe('rolesManagementApp', () => {
expect(docTitle.reset).not.toHaveBeenCalled();
expect(container).toMatchInlineSnapshot(`
<div>
Role Edit Page: {"action":"edit","roleName":"role@name","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"fieldCache":{}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/role@name","search":"","hash":""}}}
Role Edit Page: {"action":"edit","roleName":"role@name","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"fieldCache":{},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/role@name","search":"","hash":""}}}
</div>
`);

Expand All @@ -154,7 +154,7 @@ describe('rolesManagementApp', () => {
expect(docTitle.reset).not.toHaveBeenCalled();
expect(container).toMatchInlineSnapshot(`
<div>
Role Edit Page: {"action":"clone","roleName":"someRoleName","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"fieldCache":{}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/clone/someRoleName","search":"","hash":""}}}
Role Edit Page: {"action":"clone","roleName":"someRoleName","rolesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"userAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"indicesAPIClient":{"fieldCache":{},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"privilegesAPIClient":{"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}}},"http":{"basePath":{"basePath":"","serverBasePath":""},"anonymousPaths":{},"externalUrl":{}},"notifications":{"toasts":{}},"fatalErrors":{},"license":{"features$":{"_isScalar":false}},"docLinks":{},"uiCapabilities":{"catalogue":{},"management":{},"navLinks":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/clone/someRoleName","search":"","hash":""}}}
</div>
`);

Expand Down