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(@lexical/utils): fix #5918 by re-exporting shared/* constants with explicit types #5920

Merged
merged 2 commits into from
Apr 19, 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
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@
"dev": "npm run dev --prefix packages/lexical-playground",
"start-test-server": "npm run preview --prefix packages/lexical-playground -- --port 4000",
"build": "node scripts/build.js",
"build-prod": "npm run clean && npm run build -- --prod",
"build-prod": "npm run clean && npm run build -- --prod && node ./scripts/validate-tsc-types.js",
"build-playground-prod": "npm run build-prod && npm run build-prod --prefix packages/lexical-playground",
"build-release": "npm run build-prod -- --release",
"build-release": "npm run build-prod -- --release && node ./scripts/validate-tsc-types.js",
"build-www": "npm run clean && npm run build -- --www && npm run build -- --www --prod && npm run prepare-www",
"build-types": "tsc -p ./tsconfig.build.json && node ./scripts/validate-tsc-types.js",
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't used by any CI step, but it is a quick way to build and validate the types without also going through the rest of the production build script.

"clean": "node scripts/clean.js",
"extract-codes": "node scripts/build.js --codes",
"flow": "node ./scripts/check-flow-types.js",
Expand Down
38 changes: 25 additions & 13 deletions packages/lexical-utils/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,38 @@ import {
LexicalEditor,
LexicalNode,
} from 'lexical';
import {IS_FIREFOX} from 'shared/environment';
// This underscore postfixing is used as a hotfix so we do not
// export shared types from this module #5918
import {CAN_USE_DOM as CAN_USE_DOM_} from 'shared/canUseDOM';
import {
CAN_USE_BEFORE_INPUT as CAN_USE_BEFORE_INPUT_,
IS_ANDROID as IS_ANDROID_,
IS_ANDROID_CHROME as IS_ANDROID_CHROME_,
IS_APPLE as IS_APPLE_,
IS_APPLE_WEBKIT as IS_APPLE_WEBKIT_,
IS_CHROME as IS_CHROME_,
IS_FIREFOX as IS_FIREFOX_,
IS_IOS as IS_IOS_,
IS_SAFARI as IS_SAFARI_,
} from 'shared/environment';
import invariant from 'shared/invariant';
import normalizeClassNames from 'shared/normalizeClassNames';

export {default as markSelection} from './markSelection';
export {default as mergeRegister} from './mergeRegister';
export {default as positionNodeOnRange} from './positionNodeOnRange';
export {$splitNode, isHTMLAnchorElement, isHTMLElement} from 'lexical';
export {CAN_USE_DOM} from 'shared/canUseDOM';
export {
CAN_USE_BEFORE_INPUT,
IS_ANDROID,
IS_ANDROID_CHROME,
IS_APPLE,
IS_APPLE_WEBKIT,
IS_CHROME,
IS_FIREFOX,
IS_IOS,
IS_SAFARI,
} from 'shared/environment';
// Hotfix to export these with inlined types #5918
export const CAN_USE_BEFORE_INPUT: boolean = CAN_USE_BEFORE_INPUT_;
export const CAN_USE_DOM: boolean = CAN_USE_DOM_;
export const IS_ANDROID: boolean = IS_ANDROID_;
export const IS_ANDROID_CHROME: boolean = IS_ANDROID_CHROME_;
export const IS_APPLE: boolean = IS_APPLE_;
export const IS_APPLE_WEBKIT: boolean = IS_APPLE_WEBKIT_;
export const IS_CHROME: boolean = IS_CHROME_;
export const IS_FIREFOX: boolean = IS_FIREFOX_;
export const IS_IOS: boolean = IS_IOS_;
export const IS_SAFARI: boolean = IS_SAFARI_;

export type DFSNode = Readonly<{
depth: number;
Expand Down
88 changes: 88 additions & 0 deletions scripts/validate-tsc-types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/**
* 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 fs = require('fs-extra');
const glob = require('glob');
const ts = require('typescript');

const pretty = process.env.CI !== 'true';

/** @type {ts.FormatDiagnosticsHost} */
const diagnosticsHost = {
getCanonicalFileName: (fn) => fn,
getCurrentDirectory: () => './',
getNewLine: () => '\n',
};

/**
* Validate that the published .d.ts types do not have dependencies
* on any private module (currently shared/*).
*
* `process.exit(1)` on failure.
*/
function validateTscTypes() {
const dtsFilesPattern = './.ts-temp/{lexical,lexical-*}/**/*.d.ts';
const dtsFiles = glob.sync(dtsFilesPattern);
if (dtsFiles.length === 0) {
console.error(
`Missing ${dtsFilesPattern}, \`npm run build-prod\` or \`npm run build-release\` first`,
);
process.exit(1);
}
/** @type {ts.Diagnostic[]} */
const diagnostics = [];
for (const fn of dtsFiles) {
// console.log(fn);
const ast = ts.createSourceFile(
fn,
fs.readFileSync(fn, 'utf-8'),
ts.ScriptTarget.Latest,
);
const checkSpecifier = (/** @type {ts.Node | undefined} */ node) => {
if (!node || node.kind !== ts.SyntaxKind.StringLiteral) {
return;
}
const specifier = /** @type {import('typescript').StringLiteral} */ (
node
);
if (/^shared(\/|$)/.test(specifier.text)) {
const start = specifier.getStart(ast);
diagnostics.push({
category: ts.DiagnosticCategory.Error,
code: Infinity,
file: ast,
length: specifier.getEnd() - start,
messageText: `Published .d.ts files must not import private module '${specifier.text}'.`,
start,
});
}
};
ast.forEachChild((node) => {
if (node.kind === ts.SyntaxKind.ExportDeclaration) {
const exportNode =
/** @type {import('typescript').ExportDeclaration} */ (node);
checkSpecifier(exportNode.moduleSpecifier);
} else if (node.kind === ts.SyntaxKind.ImportDeclaration) {
const importNode =
/** @type {import('typescript').ImportDeclaration} */ (node);
checkSpecifier(importNode.moduleSpecifier);
}
});
}
if (diagnostics.length > 0) {
const msg = (
pretty ? ts.formatDiagnosticsWithColorAndContext : ts.formatDiagnostics
)(diagnostics, diagnosticsHost);
console.error(msg.replace(/ TSInfinity:/g, ':'));
process.exit(1);
}
}

validateTscTypes();
Loading