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

Build: Output package type declarations #18942

Merged
merged 65 commits into from
Mar 27, 2020
Merged

Build: Output package type declarations #18942

merged 65 commits into from
Mar 27, 2020

Conversation

sirreal
Copy link
Member

@sirreal sirreal commented Dec 5, 2019

Description

Output TypeScript declaration files (.d.ts) from package sources and expose them with published packages.

TypeScript 3.7 added support for outputting a declaration files from JavaScript sources.

The setup is largely inspired by the jest setup, which is another monorepo which has many TypeScript packages they generate types from.

  • Upgrade to typescript@3.8.
  • Add bin for typechecking changed packages.
  • Remove lint-types script, replaced by build:package-types.
  • Restructure TypeScript configuration:
    • One base tsconfig.base.json that packages extend. This is not a project, it's only for extension.
    • A root tsconfig.json that references all typed packages.
    • Typed packages output their types at build-types and expose them in the published package.
    • Use project references to work within the monorepo (packages reference each other).
  • Fix/improve some type issues.

TypeScript declaration files will be managed via tsc --build, the TypeScript compiler build tool for composite projects. tsc will manage determine the correct order to build packages so that dependencies are satisfied and rebuild only when necessary.

Additional packages can opt-in to typing by (this is documented in a new section in packages/README.md):

  • Adding tsconfig.json in their package directory.
  • Updating their package.json to point to the types: { "types": "build-types" }.
  • Adding a reference to the root tsconfig.json.

Testing

Verify the following scripts work as expected:

  • npm run build:package-types - Build package types. Check for a generated declaration files, e.g. packages/a11y/build-types/*.d.ts.
  • npm run clean:package-types - build-types/**/*.d.ts and *.tsbuildinfo files should be removed.
  • npm run build:packages should generate the types
  • Changes to package types should result in updated declarations
  • Repeated type builds should complete without error.

Generated type definitions can be inspected under packages/*/build-types. For example:

@wordpress/is-shallow-equal

Generated:

index.d.ts

export = isShallowEqual;
/**
 * @typedef {{[key: string]: any}} ComparableObject
 */
/**
 * Returns true if the two arrays or objects are shallow equal, or false
 * otherwise.
 *
 * @param {any[]|ComparableObject} a First object or array to compare.
 * @param {any[]|ComparableObject} b Second object or array to compare.
 *
 * @return {boolean} Whether the two values are shallow equal.
 */
declare function isShallowEqual(a: any[] | {
    [key: string]: any;
}, b: any[] | {
    [key: string]: any;
}): boolean;
declare namespace isShallowEqual {
    export { isShallowEqualObjects, isShallowEqualArrays, ComparableObject };
}
/**
 * Internal dependencies;
 */
declare var isShallowEqualObjects: typeof import("./objects");
declare var isShallowEqualArrays: typeof import("./arrays");
type ComparableObject = {
    [key: string]: any;
};
//# sourceMappingURL=index.d.ts.map

arrays.d.ts

export = isShallowEqualArrays;
/**
 * Returns true if the two arrays are shallow equal, or false otherwise.
 *
 * @param {any[]} a First array to compare.
 * @param {any[]} b Second array to compare.
 *
 * @return {boolean} Whether the two arrays are shallow equal.
 */
declare function isShallowEqualArrays(a: any[], b: any[]): boolean;
//# sourceMappingURL=arrays.d.ts.map

objects.d.ts

export = isShallowEqualObjects;
/**
 * Returns true if the two objects are shallow equal, or false otherwise.
 *
 * @param {import('.').ComparableObject} a First object to compare.
 * @param {import('.').ComparableObject} b Second object to compare.
 *
 * @return {boolean} Whether the two objects are shallow equal.
 */
declare function isShallowEqualObjects(a: {
    [key: string]: any;
}, b: {
    [key: string]: any;
}): boolean;
//# sourceMappingURL=objects.d.ts.map

Manual type declaration:

// Type definitions for @wordpress/is-shallow-equal 1.4
// Project: https://github.com/WordPress/gutenberg/tree/master/packages/is-shallow-equal/README.md
// Definitions by: Derek Sifford <https://github.com/dsifford>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 3.5

declare const isShallowEqual: {
    /**
     * Returns `true` if the two arrays or objects are shallow equal, or `false` otherwise.
     *
     * @param a - First object or array to compare.
     * @param b - Second object or array to compare.
     *
     * @returns Whether the two values are shallow equal.
     */
    (a: ArrayLike<any> | object, b: ArrayLike<any> | object): boolean;

    /**
     * Returns true if the two arrays are shallow equal, or false otherwise.
     *
     * @param a - First array to compare.
     * @param b - Second array to compare.
     *
     * @returns Whether the two arrays are shallow equal.
     */
    isShallowEqualArrays(a: ArrayLike<any>, b: ArrayLike<any>): boolean;

    /**
     * Returns `true` if the two objects are shallow equal, or `false` otherwise.
     *
     * @param a - First object to compare.
     * @param b - Second object to compare.
     *
     * @returns Whether the two objects are shallow equal.
     */
    isShallowEqualObjects(a: object, b: object): boolean;
};

export = isShallowEqual;
@wordpress/i18n

Generated

index.d.ts

/**
 * Merges locale data into the Tannin instance by domain. Accepts data in a
 * Jed-formatted JSON object shape.
 *
 * @see http://messageformat.github.io/Jed/
 *
 * @param {LocaleData} [data]   Locale data configuration.
 * @param {string}     [domain] Domain for which configuration applies.
 */
export function setLocaleData(data?: {
    [key: string]: any;
} | undefined, domain?: string | undefined): void;
/**
 * Retrieve the translation of text.
 *
 * @see https://developer.wordpress.org/reference/functions/__/
 *
 * @param {string} text     Text to translate.
 * @param {string} [domain] Domain to retrieve the translated text.
 *
 * @return {string} Translated text.
 */
export function __(text: string, domain?: string | undefined): string;
/**
 * Retrieve translated string with gettext context.
 *
 * @see https://developer.wordpress.org/reference/functions/_x/
 *
 * @param {string} text     Text to translate.
 * @param {string} context  Context information for the translators.
 * @param {string} [domain] Domain to retrieve the translated text.
 *
 * @return {string} Translated context string without pipe.
 */
export function _x(text: string, context: string, domain?: string | undefined): string;
/**
 * Translates and retrieves the singular or plural form based on the supplied
 * number.
 *
 * @see https://developer.wordpress.org/reference/functions/_n/
 *
 * @param {string} single   The text to be used if the number is singular.
 * @param {string} plural   The text to be used if the number is plural.
 * @param {number} number   The number to compare against to use either the
 *                          singular or plural form.
 * @param {string} [domain] Domain to retrieve the translated text.
 *
 * @return {string} The translated singular or plural form.
 */
export function _n(single: string, plural: string, number: number, domain?: string | undefined): string;
/**
 * Translates and retrieves the singular or plural form based on the supplied
 * number, with gettext context.
 *
 * @see https://developer.wordpress.org/reference/functions/_nx/
 *
 * @param {string} single   The text to be used if the number is singular.
 * @param {string} plural   The text to be used if the number is plural.
 * @param {number} number   The number to compare against to use either the
 *                          singular or plural form.
 * @param {string} context  Context information for the translators.
 * @param {string} [domain] Domain to retrieve the translated text.
 *
 * @return {string} The translated singular or plural form.
 */
export function _nx(single: string, plural: string, number: number, context: string, domain?: string | undefined): string;
/**
 * Check if current locale is RTL.
 *
 * **RTL (Right To Left)** is a locale property indicating that text is written from right to left.
 * For example, the `he` locale (for Hebrew) specifies right-to-left. Arabic (ar) is another common
 * language written RTL. The opposite of RTL, LTR (Left To Right) is used in other languages,
 * including English (`en`, `en-US`, `en-GB`, etc.), Spanish (`es`), and French (`fr`).
 *
 * @return {boolean} Whether locale is RTL.
 */
export function isRTL(): boolean;
/**
 * Returns a formatted string. If an error occurs in applying the format, the
 * original format string is returned.
 *
 * @param {string}    format The format of the string to generate.
 * @param {...string} args   Arguments to apply to the format.
 *
 * @see http://www.diveintojavascript.com/projects/javascript-sprintf
 *
 * @return {string} The formatted string.
 */
export function sprintf(format: string, ...args: string[]): string;
export type LocaleData = {
    [key: string]: any;
};
//# sourceMappingURL=index.d.ts.map

Manual

// Type definitions for @wordpress/i18n 3.4
// Project: https://github.com/WordPress/gutenberg/tree/master/packages/i18n/README.md
// Definitions by: Derek Sifford <https://github.com/dsifford>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped

/**
 * Merges locale data into the Tannin instance by domain. Accepts data in a
 * Jed-formatted JSON object shape.
 *
 * @see http://messageformat.github.io/Jed/
 *
 * @param data   Locale data configuration.
 * @param domain Domain for which configuration applies.
 */
export function setLocaleData(data: any, domain?: string): void;

/**
 * Retrieve the translation of text.
 *
 * @see https://developer.wordpress.org/reference/functions/__/
 *
 * @param  text   Text to translate.
 * @param  domain Domain to retrieve the translated text.
 */
export function __(text: string, domain?: string): string;

/**
 * Retrieve translated string with gettext context.
 *
 * @see https://developer.wordpress.org/reference/functions/_x/
 *
 * @param  text    Text to translate.
 * @param  context Context information for the translators.
 * @param  domain  Domain to retrieve the translated text.
 */
export function _x(text: string, context: string, domain?: string): string;

/**
 * Translates and retrieves the singular or plural form based on the supplied
 * number.
 *
 * @see https://developer.wordpress.org/reference/functions/_n/
 *
 * @param  single The text to be used if the number is singular.
 * @param  plural The text to be used if the number is plural.
 * @param  number The number to compare against to use either the
 *                         singular or plural form.
 * @param  domain Domain to retrieve the translated text.
 */
export function _n(
    single: string,
    plural: string,
    n: number,
    domain?: string
): string;

/**
 * Translates and retrieves the singular or plural form based on the supplied
 * number, with gettext context.
 *
 * @see https://developer.wordpress.org/reference/functions/_nx/
 *
 * @param  single  The text to be used if the number is singular.
 * @param  plural  The text to be used if the number is plural.
 * @param  number  The number to compare against to use either the
 *                          singular or plural form.
 * @param  context Context information for the translators.
 * @param  domain  Domain to retrieve the translated text.
 */
export function _nx(
    single: string,
    plural: string,
    n: number,
    context: string,
    domain?: string
): string;

/**
 * Returns a formatted string. If an error occurs in applying the format, the
 * original format string is returned.
 *
 * @param  format  The format of the string to generate.
 * @param  args Arguments to apply to the format.
 *
 * @see http://www.diveintojavascript.com/projects/javascript-sprintf
 */
export function sprintf(format: string, ...args: any[]): string;

Try using some of the typed packages in an existing project, preferably via TypeScript. Are the type definitions correctly exposed?

@github-actions
Copy link

github-actions bot commented Feb 27, 2020

Size Change: +176 B (0%)

Total Size: 856 kB

Filename Size Change
build/annotations/index.js 3.44 kB +18 B (0%)
build/block-editor/index.js 101 kB +7 B (0%)
build/block-library/index.js 110 kB +59 B (0%)
build/block-library/style-rtl.css 7.47 kB +41 B (0%)
build/block-library/style.css 7.48 kB +42 B (0%)
build/token-list/index.js 1.28 kB +9 B (0%)
ℹ️ View Unchanged
Filename Size Change
build/a11y/index.js 998 B 0 B
build/api-fetch/index.js 3.39 kB 0 B
build/autop/index.js 2.58 kB 0 B
build/blob/index.js 620 B 0 B
build/block-directory/index.js 6.02 kB 0 B
build/block-directory/style-rtl.css 760 B 0 B
build/block-directory/style.css 760 B 0 B
build/block-editor/style-rtl.css 10.9 kB 0 B
build/block-editor/style.css 10.9 kB 0 B
build/block-library/editor-rtl.css 7.22 kB 0 B
build/block-library/editor.css 7.23 kB 0 B
build/block-library/theme-rtl.css 669 B 0 B
build/block-library/theme.css 671 B 0 B
build/block-serialization-default-parser/index.js 1.65 kB 0 B
build/block-serialization-spec-parser/index.js 3.1 kB 0 B
build/blocks/index.js 57.5 kB 0 B
build/components/index.js 190 kB 0 B
build/components/style-rtl.css 15.8 kB 0 B
build/components/style.css 15.7 kB 0 B
build/compose/index.js 6.21 kB 0 B
build/core-data/index.js 10.6 kB 0 B
build/data-controls/index.js 1.04 kB 0 B
build/data/index.js 8.25 kB 0 B
build/date/index.js 5.37 kB 0 B
build/deprecated/index.js 771 B 0 B
build/dom-ready/index.js 568 B 0 B
build/dom/index.js 3.06 kB 0 B
build/edit-post/index.js 91.2 kB 0 B
build/edit-post/style-rtl.css 8.43 kB 0 B
build/edit-post/style.css 8.43 kB 0 B
build/edit-site/index.js 6.73 kB 0 B
build/edit-site/style-rtl.css 2.91 kB 0 B
build/edit-site/style.css 2.9 kB 0 B
build/edit-widgets/index.js 4.43 kB 0 B
build/edit-widgets/style-rtl.css 2.57 kB 0 B
build/edit-widgets/style.css 2.57 kB 0 B
build/editor/editor-styles-rtl.css 428 B 0 B
build/editor/editor-styles.css 431 B 0 B
build/editor/index.js 42.8 kB 0 B
build/editor/style-rtl.css 3.38 kB 0 B
build/editor/style.css 3.38 kB 0 B
build/element/index.js 4.44 kB 0 B
build/escape-html/index.js 733 B 0 B
build/format-library/index.js 6.95 kB 0 B
build/format-library/style-rtl.css 502 B 0 B
build/format-library/style.css 502 B 0 B
build/hooks/index.js 1.93 kB 0 B
build/html-entities/index.js 622 B 0 B
build/i18n/index.js 3.49 kB 0 B
build/is-shallow-equal/index.js 710 B 0 B
build/keyboard-shortcuts/index.js 2.3 kB 0 B
build/keycodes/index.js 1.69 kB 0 B
build/list-reusable-blocks/index.js 2.99 kB 0 B
build/list-reusable-blocks/style-rtl.css 226 B 0 B
build/list-reusable-blocks/style.css 226 B 0 B
build/media-utils/index.js 4.84 kB 0 B
build/notices/index.js 1.57 kB 0 B
build/nux/index.js 3.01 kB 0 B
build/nux/style-rtl.css 616 B 0 B
build/nux/style.css 613 B 0 B
build/plugins/index.js 2.54 kB 0 B
build/primitives/index.js 1.5 kB 0 B
build/priority-queue/index.js 781 B 0 B
build/redux-routine/index.js 2.84 kB 0 B
build/rich-text/index.js 14.5 kB 0 B
build/server-side-render/index.js 2.55 kB 0 B
build/shortcode/index.js 1.7 kB 0 B
build/url/index.js 4.01 kB 0 B
build/viewport/index.js 1.61 kB 0 B
build/warning/index.js 1.14 kB 0 B
build/wordcount/index.js 1.18 kB 0 B

compressed-size-action

@sirreal sirreal marked this pull request as ready for review February 27, 2020 18:02
@sirreal sirreal added the Needs Technical Feedback Needs testing from a developer perspective. label Feb 27, 2020
@Tug
Copy link
Contributor

Tug commented Mar 27, 2020

I did a quick sanity check and it looks good for gutenberg-mobile as of now 👍

It might impact the Monorepo work that's being done though as we needed to do some changes in tsconfig.json to fix a conflict with react-native types. I guess we can address that ourselves though and ping you if needed ;)

@gziolo
Copy link
Member

gziolo commented Mar 27, 2020

Travis is green, there is approval from @aduth. I will leave it to you both to push the button and celebrate this moment. Awesome work 🥇

I'm looking forward to starting using it in practice – I wasn't very happy by type support in Visual Studio Code for code imported from local npm packages. It should make it way much better! :)

@aduth
Copy link
Member

aduth commented Mar 27, 2020

I wasn't very happy by type support in Visual Studio Code for code imported from local npm packages. It should make it way much better! :)

Initially it will be better in some ways (up-to-date) and worse in others (our usage of types currently is not very good), but it will definitely force us to be more honest with how and where we use types. I imagine it will only get better over time! 🚀

Copy link
Member

@aduth aduth left a comment

Choose a reason for hiding this comment

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

Still looking good 👍

@sirreal sirreal merged commit 65520ef into master Mar 27, 2020
@github-actions github-actions bot added this to the Gutenberg 7.9 milestone Mar 27, 2020
@sirreal
Copy link
Member Author

sirreal commented Mar 27, 2020

Thanks everyone who helped get this over the line ❤️ 🙌

@aduth aduth mentioned this pull request Mar 27, 2020
sirreal added a commit that referenced this pull request Mar 27, 2020
Provide clear errors due to mismated TypeScript versions. This should help make the transition period from #18942 smoother.

Add `validate-typescript-version` bin that will be used by `lint-staged` typechecker and in `build:package-types` script.

This helps to prevent cryptic errors when an older TypeScript module is present in `node_modules` and requires updating.

Without this change, lint-staged and `npm run build:package-types` would print this error:

```none
# npm run build:pacakge-types
> tsc --build

error TS5053: Option 'allowJs' cannot be specified with option 'declaration'.
error TS5053: Option 'declaration' cannot be specified with option 'isolatedModules'.
```

This is due to an incompatible TypeScript package. With this change, these script will print this error:

```none
$ npm run build:package-types
> node ./bin/packages/validate-typescript-version.js && tsc --build

TypeScript dependency out of date.
        Detected: '3.5.3'
        Required: '3.8.3'
Please ensure dependencies are up to date.
```
sirreal added a commit that referenced this pull request Mar 27, 2020
@sirreal sirreal added [Type] Build Tooling Issues or PRs related to build tooling [Type] Enhancement A suggestion for improvement. npm Packages Related to npm packages and removed Needs Technical Feedback Needs testing from a developer perspective. [Type] Technical Prototype Offers a technical exploration into an idea as an example of what's possible labels Apr 1, 2020
@sirreal sirreal deleted the try/output-typedefs branch April 3, 2020 20:47
@gziolo gziolo mentioned this pull request Nov 10, 2020
6 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
npm Packages Related to npm packages [Type] Build Tooling Issues or PRs related to build tooling [Type] Code Quality Issues or PRs that relate to code quality [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants