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

fix(build): clean up of build-www rewriteImports (follow-up to #5995) #5999

Merged
merged 3 commits into from
May 2, 2024
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
107 changes: 107 additions & 0 deletions scripts/www/__tests__/unit/transformFlowFileContents.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/
// @ts-check
'use strict';

const transformFlowFileContents = require('../../transformFlowFileContents');

const HEADER_BEFORE =
`
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
*/
`.trim() + '\n';

const HEADER_AFTER =
`
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @generated
* @oncall lexical_web_text_editor
*/
`.trim() + '\n';

const IMPORTS_BEFORE =
`
import type {Doc, RelativePosition, UndoManager, XmlText} from 'yjs';
import type {
DecoratorNode,
EditorState,
ElementNode,
LexicalCommand,
LexicalEditor,
LexicalNode,
LineBreakNode,
NodeMap,
NodeKey,
TextNode,
} from 'lexical';
`.trim() + '\n';

const IMPORTS_AFTER =
`
import type {Doc, RelativePosition, UndoManager, XmlText} from 'yjs';
import type {
DecoratorNode,
EditorState,
ElementNode,
LexicalCommand,
LexicalEditor,
LexicalNode,
LineBreakNode,
NodeMap,
NodeKey,
TextNode,
} from 'Lexical';
`.trim() + '\n';

const EXTRA_BLOCK_COMMENT =
`
/**
* LexicalDevToolsCore
*/
`.trim() + '\n';

describe('transformFlowFileContents', () => {
[
{
input: [HEADER_BEFORE, IMPORTS_BEFORE, EXTRA_BLOCK_COMMENT].join('\n'),
output: [HEADER_AFTER, IMPORTS_AFTER, EXTRA_BLOCK_COMMENT].join('\n'),
title: 'header-imports-comment',
},
{
input: [HEADER_BEFORE, EXTRA_BLOCK_COMMENT].join('\n'),
output: [HEADER_AFTER, EXTRA_BLOCK_COMMENT].join('\n'),
title: 'header-comment',
},
{
input: [HEADER_BEFORE, IMPORTS_BEFORE].join('\n'),
output: [HEADER_AFTER, IMPORTS_AFTER].join('\n'),
title: 'header-imports',
},
{
input: [HEADER_BEFORE].join('\n'),
output: [HEADER_AFTER].join('\n'),
title: 'header',
},
].forEach(({input, output, title}) => {
it(`transforms ${title}`, async () => {
expect(await transformFlowFileContents(input)).toBe(output);
});
});
});
54 changes: 1 addition & 53 deletions scripts/www/rewriteImports.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,59 +12,7 @@ const fs = require('fs-extra');
const glob = require('glob');
const path = require('node:path');
const {packagesManager} = require('../shared/packagesManager');
const npmToWwwName = require('./npmToWwwName');
const {t, transform} = require('hermes-transform');

const wwwMappings = Object.fromEntries(
packagesManager
.getPublicPackages()
.flatMap((pkg) =>
pkg.getExportedNpmModuleNames().map((npm) => [npm, npmToWwwName(npm)]),
),
);

/**
* It would be nice to use jscodeshift for this but the flow sources are using
* ast features that are not supported in ast-types (as of 2024-04-11) so it's
* not possible to traverse the tree and replace the imports & comments.
*
* It might be possible going straight to flow-parser, but it was a slew of
* hardcoded regexps before and now it's at least automated based on the
* exports.
*
* @param {string} source
* @returns {Promise<string>} transformed source
*/
async function transformFlowFileContents(source) {
return await transform(
source,
(context) => ({
ImportDeclaration(node) {
const value = wwwMappings[node.source.value];
if (value) {
context.replaceNode(node.source, t.StringLiteral({value}));
}
},
Program(node) {
if (
node.docblock &&
node.docblock.comment &&
node.docblock.comment.value.includes('@flow strict')
) {
node.docblock.comment.value = node.docblock.comment.value.replace(
/ \* @flow strict/g,
' * @flow strict\n * @generated\n * @oncall lexical_web_text_editor',
);
// Let the transform know we actually did something.
// Could not figure out the right way to update the
// docblock without an in-place update
context.addLeadingComments(node, '');
}
},
}),
{},
);
}
const transformFlowFileContents = require('./transformFlowFileContents');

// This script attempts to find all Flow definition modules, and makes
// them compatible with www. Specifically, it finds any imports that
Expand Down
90 changes: 90 additions & 0 deletions scripts/www/transformFlowFileContents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

'use strict';

const {packagesManager} = require('../shared/packagesManager');
const npmToWwwName = require('./npmToWwwName');
const {t, transform} = require('hermes-transform');
const prettier = require('prettier');

const wwwMappings = Object.fromEntries(
packagesManager
.getPublicPackages()
.flatMap((pkg) =>
pkg.getExportedNpmModuleNames().map((npm) => [npm, npmToWwwName(npm)]),
),
);

const prettierConfig = prettier.resolveConfig('./').then((cfg) => cfg || {});

/**
* Add a statement to the end of the code so the comments don't
* disappear. This is a workaround for a hermes transform issue.
*
* @param {string} code
*/
function wrapCode(code) {
return [code, 'export {};\n'].join('\n');
}

/**
* The inverse transform of wrapCode, removes the added statement.
*
* @param {string} code
*/
function unwrapCode(code) {
return code.replace(/\n+export {};\n?$/, '\n');
}

/**
* It would be nice to use jscodeshift for this but the flow sources are using
* ast features that are not supported in ast-types (as of 2024-04-11) so it's
* not possible to traverse the tree and replace the imports & comments.
*
* It might be possible going straight to flow-parser, but it was a slew of
* hardcoded regexps before and now it's at least automated based on the
* exports.
*
* @param {string} source
* @returns {Promise<string>} transformed source
*/
module.exports = async function transformFlowFileContents(source) {
return unwrapCode(
await transform(
wrapCode(source),
(context) => ({
ImportDeclaration(node) {
const value = wwwMappings[node.source.value];
if (value) {
context.replaceNode(node.source, t.StringLiteral({value}));
}
},
Program(node) {
if (
node.docblock &&
node.docblock.comment &&
node.docblock.comment.value.includes('@flow strict')
) {
// This is mutated in-place because I couldn't find a mutation that
// did not fail for replacing the Program node.
node.docblock.comment.value = node.docblock.comment.value.replace(
/ \* @flow strict/g,
' * @flow strict\n * @generated\n * @oncall lexical_web_text_editor',
);
// We need the mutations array to be non-empty, so remove something
// that is not there. The AST traversals use object identity in a
// Set so we don't have to worry about some other line changing.
context.removeComments(t.LineComment({value: ''}));
}
},
}),
await prettierConfig,
),
);
};
Loading