Skip to content
This repository was archived by the owner on Jan 13, 2025. It is now read-only.

chore: replace JS for TS #4435

Closed
wants to merge 25 commits into from
Closed

Conversation

moog16
Copy link
Contributor

@moog16 moog16 commented Feb 19, 2019

related #4225

I'm fairly certain I found all instances of .js that needed to be replaced to .ts. I ignored all demos/tests since we aren't rewriting those just yet.

TODO:

  • revisit the check-imports.js script

@codecov-io
Copy link

codecov-io commented Feb 19, 2019

Codecov Report

Merging #4435 into feat/typescript will increase coverage by <.01%.
The diff coverage is n/a.

Impacted file tree graph

@@                 Coverage Diff                 @@
##           feat/typescript    #4435      +/-   ##
===================================================
+ Coverage            99.05%   99.05%   +<.01%     
===================================================
  Files                  130      130              
  Lines                 6238     6239       +1     
  Branches               813      813              
===================================================
+ Hits                  6179     6180       +1     
  Misses                  58       58              
  Partials                 1        1
Impacted Files Coverage Δ
packages/mdc-radio/component.ts 86.95% <0%> (ø) ⬆️
packages/mdc-menu-surface/foundation.ts 100% <0%> (ø) ⬆️
packages/mdc-checkbox/component.ts 95.74% <0%> (ø) ⬆️
packages/mdc-auto-init/index.ts 95.45% <0%> (+0.21%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c6b609c...7cd0007. Read the comment docs.

Copy link
Contributor

@kfranqueiro kfranqueiro left a comment

Choose a reason for hiding this comment

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

After starting to look through this PR, I think we should leave the Getting Started guide for a separate PR, if we touch it at all. The most important question is, will it continue to work as-is? (Since that's the entire point of us continuing to build the ESM where the ES2015+ source files used to reside.)

I'm concerned that the Getting Started guide becomes even more overwhelming and complicated than it already is, and if someone's interested in writing TS themselves, I feel like there should be separate resources for that beyond the scope of our repository.

@@ -1,179 +0,0 @@
<!--docs:
Copy link
Contributor

Choose a reason for hiding this comment

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

Are you sure we should remove this file...?

I had already updated this in an attempt to filter it down to things that still seemed relevant.

@acdvorak does this file still have merit for purposes of avoiding breakages when we mirror code internally? For that matter is there any stuff missing here that should be noted? (Or is anything that was notable in here present in best_practices.md?)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Well if we want to keep this as a best practices for writing foundations/adapter/components with TypeScript that makes sense. But a lot of this seems to be covered in the authoring-components.md. What do you think?

@@ -149,7 +149,7 @@ And open http://localhost:8080 in a browser. You should see a blue “Hello Worl
Now that you have webpack configured to compile Sass into CSS, let's include the Sass files for the Material Design button. First install the Node dependency:

```
npm install --save-dev @material/button
Copy link
Contributor

Choose a reason for hiding this comment

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

Don't remove --save-dev. I'm pretty sure I stopped someone from doing this before (or had them revert it).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why are we telling people to add their non-build step deps to devDeps? dependencies are modules which are also required at runtime.

You can install all of them by running this command:

```
npm install --save-dev babel-core@6 babel-loader@7 babel-preset-es2015 babel-plugin-transform-object-assign
npm install --save-dev babel-core@6 babel-loader@7 babel-preset-es2015 babel-plugin-transform-object-assign [email protected]
Copy link
Contributor

Choose a reason for hiding this comment

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

Should typescript also be installed locally? It's not in this list...

Also, babel-plugin-transform-object-assign should no longer be needed (we should test that - I'm pretty sure all instances of Object.assign should no longer exist on the feat/typescript branch)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmm...ya i suppose it should. Below I also recommend installing globally, but it should be included in their deps in package.json

@mdc-web-bot
Copy link
Collaborator

All 624 screenshot tests passed for commit efdd684 vs. feat/typescript! 💯🎉

@mdc-web-bot
Copy link
Collaborator

All 624 screenshot tests passed for commit 7d9fed4 vs. feat/typescript! 💯🎉

@mdc-web-bot
Copy link
Collaborator

All 624 screenshot tests passed for commit c5206d7 vs. feat/typescript! 💯🎉

@mdc-web-bot
Copy link
Collaborator

All 624 screenshot tests passed for commit 9963010 vs. feat/typescript! 💯🎉

@mdc-web-bot
Copy link
Collaborator

All 624 screenshot tests passed for commit 380bdd0 vs. feat/typescript! 💯🎉

Note that in this case, you must ensure your build toolchain is configured to process and transpile MDC Web's modules
as well as your own. You will also need to include babel's
[`transform-object-assign`](https://www.npmjs.com/package/babel-plugin-transform-object-assign) plugin for IE 11 support.

See the [Getting Started guide](getting-started.md) for more details on setting up an environment.

#### TypeScript

If you are using TypeScript, MDC Web's packages also include `.d.ts` files for your consumption. Most of the time you shouldn't need to reference these, as the TypeScript compiler should automatically pick them up. MDC Web has also defined the `types` property found in `package.json`, for your convinence. There is a bundled `.d.ts` file found under the `dist` directory that maps to the respective UMD module. There are corresponding `.d.ts` files for each foundation/component/adapter/etc. within the package.
Copy link
Contributor

Choose a reason for hiding this comment

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

Spelling: convinence. Also, what does "for your convenience" mean? Why is the types property important; what does it do?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I read this doc again: https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html

It reads, "Also note that if your main declaration file is named index.d.ts and lives at the root of the package (next to index.js) you do not need to mark the "types" property, though it is advisable to do so."

I've been reading this incorrectly, and its actually required in our case. I'll update the wording.

setToggleColorTextContent: (/* textContent: string */) => {},
registerInteractionHandler: (/* type: string, handler: EventListener */) => {},
deregisterInteractionHandler: (/* type: string, handler: EventListener */) => {}
setAttr: (/* attr: string, value: string */) => undefined,
Copy link
Contributor

Choose a reason for hiding this comment

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

Replace Closure Doc comments with TypeScript

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I replaced it with:

      getAttr: (attr: string) => '',
      setAttr: (attr: string, value: string) => undefined,
      addClass: (className: string) => undefined,
      removeClass: (className: string) => undefined,
      setToggleColorTextContent: (textContent: string) => undefined,
      registerInteractionHandler: (type: string, handler: EventListener) => undefined,
      deregisterInteractionHandler: (type: string, handler: EventListener) => undefined

Although in our actual implementation we don't have unused arguments. Should I just remove them to look like:

      getAttr: () => '',
      setAttr: () => undefined,
      addClass: () => undefined,
      removeClass: () => undefined,
      setToggleColorTextContent: () => undefined,
      registerInteractionHandler: () => undefined,
      deregisterInteractionHandler: () => undefined,

Copy link
Contributor

Choose a reason for hiding this comment

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

I would probably leave them to make it clear that they exist, even if we're not using them.

@mdc-web-bot
Copy link
Collaborator

All 624 screenshot tests passed for commit aa80f74 vs. feat/typescript! 💯🎉

@acdvorak
Copy link
Contributor

We can probably update check-imports.js in PR #4449.

Copy link
Contributor

@acdvorak acdvorak left a comment

Choose a reason for hiding this comment

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

A few small wording suggestions, but otherwise LGTM!

├── constants.js # The component's cssClasses/strings/numbers constants
├── foundation.js # The component's foundation class
├── index.js # The file that contains the vanilla component class, as well as exports the foundation
├── adapter.ts # The adapter interface implemented by the component
Copy link
Contributor

Choose a reason for hiding this comment

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

# Adapter interface implemented by framework wrappers and the vanilla component

Copy link
Contributor

Choose a reason for hiding this comment

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

Were some of Andy's comments not resolved?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm just about to fix these. I didn't see this yesterday.

├── foundation.js # The component's foundation class
├── index.js # The file that contains the vanilla component class, as well as exports the foundation
├── adapter.ts # The adapter interface implemented by the component
├── foundation.ts # The component's foundation class
Copy link
Contributor

Choose a reason for hiding this comment

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

# Framework-agnostic business logic used by wrapper libraries and the vanilla component

@@ -478,9 +474,11 @@ A typical component within our codebase looks like so:
packages
├── mdc-component
├── README.md # The component's README
Copy link
Contributor

Choose a reason for hiding this comment

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

# Usage instructions and API documentation

├── foundation.ts # The component's foundation class
├── component.ts # The file that contains the vanilla component class
├── constants.ts # The component's cssClasses/strings/numbers constants
├── index.ts # The file that exports the source files of the package (foundation, component, adapter, constants, etc.)
├── mdc-component.scss # The main source file for the component's CSS
Copy link
Contributor

Choose a reason for hiding this comment

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

Let's add line items for types.ts and util.ts too. Be sure to mention that they're both optional.

All non-component-related types and interfaces not covered by other files should go in types.ts.

util.ts is for framework-agnostic helper functions (e.g., feature detection).

├── index.js # The file that contains the vanilla component class, as well as exports the foundation
├── adapter.ts # The adapter interface implemented by the component
├── foundation.ts # The component's foundation class
├── component.ts # The file that contains the vanilla component class
Copy link
Contributor

Choose a reason for hiding this comment

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

# Vanilla adapter implementation for clients who aren't using a wrapper framework like React

├── adapter.ts # The adapter interface implemented by the component
├── foundation.ts # The component's foundation class
├── component.ts # The file that contains the vanilla component class
├── constants.ts # The component's cssClasses/strings/numbers constants
Copy link
Contributor

Choose a reason for hiding this comment

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

# Constant values used by one or more files in the package (e.g., cssClasses, strings, numbers)

├── foundation.ts # The component's foundation class
├── component.ts # The file that contains the vanilla component class
├── constants.ts # The component's cssClasses/strings/numbers constants
├── index.ts # The file that exports the source files of the package (foundation, component, adapter, constants, etc.)
Copy link
Contributor

Choose a reason for hiding this comment

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

# Re-exports types from other files in the package (adapter, foundation, component, util, etc.)

@@ -26,12 +26,20 @@ of your built assets, you will want to explicitly reference the package's `index
import {MDCFoo, MDCFooFoundation} from '@material/foo/index';
```

Certain build tools will often pick up on the use of `package.json`'s `module` property, which points to the ES2015+ `index.js` source code. If you are using [Webpack](https://webpack.js.org/) or [Rollup](https://rollupjs.org/guide/en), then you will not need to reference the `index.js` file, and can continue to use the `@material/foo` import paths.
Copy link
Contributor

Choose a reason for hiding this comment

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

Certain build tools will detect the `module` property in `package.json`,
which points to ES2015+ source code in an `index.js` file.
If you're using [Webpack](https://webpack.js.org/) or
[Rollup](https://rollupjs.org/guide/en), you do not need to
reference `/index` directly, and can continue to use the shorter
`@material/foo` import path syntax.

(Reformat the above into one line)

Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like this comment hasn't been applied.

Furthermore, index.js isn't ES2015+ source anymore. It's ES5 source in an ES module.

Copy link
Contributor

Choose a reason for hiding this comment

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

How would you phrase "ES5 source in an ES module" in context?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The 2 index.js files of the npm package are:

dist/index.js (ES5) -- UMD
index.js (ES2015+) -- ESModule

Isn't what Andy wrote correct?

Copy link
Contributor

Choose a reason for hiding this comment

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

The distinction is that prior to the TS conversion, *.js was ES modules with ES2015+ syntax, thus requiring babel and additionally the object assign polyfill. After the TS conversion, *.js are ES modules with ES5 syntax. I believe that means you can get away with just webpack and not babel at all?

In terms of phrasing, something like: "which points to an ES module otherwise containing only ES5 syntax". We could link something to Rollup's pkg.module definition at https://github.com/rollup/rollup/wiki/pkg.module if we want.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@kfranqueiro I just checked by running npm run build:esmodules, so I do think you're correct.

Also I just noticed that we don't use Object.assign anywhere in our packages/ directory. I do not think you need babel for either of our UMD or ESM packages.

Copy link
Contributor

Choose a reason for hiding this comment

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

RE your last point, that's what I mean. Previously we were relying on the object assign polyfill to generate our UMD (you were never expected to need babel to consume those). Now we don't need to use that polyfill for our build process, and neither does anyone else using us within their own.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In getting-started.md there is this dep:

- [babel-plugin-transform-object-assign](http://npmjs.org/package/babel-plugin-transform-object-assign), for IE 11 support

I am going to remove it

class RedblueToggle extends MDCComponent {
initialize() {
this.root.addEventListener('click', this.foundation_.handleClick);
Copy link
Contributor

Choose a reason for hiding this comment

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

These can use this.listen / this.unlisten here now that the class extends MDCComponent

├── constants.js # The component's cssClasses/strings/numbers constants
├── foundation.js # The component's foundation class
├── index.js # The file that contains the vanilla component class, as well as exports the foundation
├── adapter.ts # The adapter interface implemented by the component
Copy link
Contributor

Choose a reason for hiding this comment

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

Were some of Andy's comments not resolved?

The following guidelines outline the general conventions for writing closure-friendly code for MDC Web.
This section should contain most - if not all - of what you need to get up and running writing components in our codebase.

### All `import` statements must _not_ use re-exported modules.
Copy link
Contributor

Choose a reason for hiding this comment

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

This should effectively be moved to best_practices.md in the form of a heading explaining that in the context of component packages, only the index and component files are allowed to reference index, because wrappers shouldn't need to pull in index or component

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is this line still accurate?

This is an unfortunate side-effect of how closure's module naming mechanism works.


Using this convention allows us to write tooling around handling these expressions, such as lint rule exceptions.

### For key/value maps, use index signatures where possible
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be moved to best_practices.md; I added this specifically for TypeScript

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Re both closure-compiler comments: I ended up adding this entire block:


#### All `import` statements must _not_ use re-exported modules

Only the `index.ts` or `component.ts` files are allowed to reference from other component packages' `index.ts`. This is because wrapping libraries only use `foundation` and `adapter`, so we should decouple the `component`.

```ts
// BAD
import {MDCFoundation} from '@material/base';
// GOOD
import {MDCFoundation} from '@material/base/foundation';

All adapters must be defined as interfaces

Each adapter must be defined within an adapter.ts file in the component's package directory.
All methods should contain a summary of what they should do. This summary should be
copied over to the adapter API documentation in our README. This will facilitate future endeavors
to potentially automate the generation of our adapter API docs. Note that this replaces the
inline comments present in the methods within defaultAdapter
.

// adapter.ts
export interface MDCComponentAdapter {
 /**
  * Adds a class to the root element.
  */
 addClass(className: string): void;
  /**
  * Removes a class from the root element.
  */
 removeClass(className: string): void;
}

Foundation classes must extend MDCFoundation

Foundations must extend MDCFoundation parameterized by their respective adapter. The
defaultAdapter must return an object with the correct adapter shape.

// foundation.ts
import {MDCFoundation} from '@material/base/foundation';
import MDCComponentAdapter from './adapter';
export class MDCComponentFoundation extends MDCFoundation<MDCComponentAdapter> {
  static get defaultAdapter(): MDCComponentAdapter {
    return {
      addClass: (className: string) => undefined,
      removeClass: (className: string) => undefined,
    };
  }
}

Component classes must extend MDCComponent

Components must extend MDCComponent parameterized by their respective foundation.

// index.ts
import {MDCComponent} from '@material/base/component';
import MDCComponentFoundation from './foundation';
export class MDCAwesomeComponent extends MDCComponent<MDCComponentFoundation> {
  getDefaultFoundation(): MDCComponentFoundation {
    return new MDCComponentFoundation({
      addClass: (className: string) => this.root_.classList.add(className),
      removeClass: (className: string) => this.root_.classList.remove(className),
    });
  }
}

For key/value maps, use index signatures where possible

Index signatures are useful for homogeneous maps of key/value pairs, while interfaces are useful for specific object signatures.
Both are more specific than just typing something as object, and are thus preferable for their respective use cases.

static get strings(): {[key: string]: string} {
  ...
}

@@ -26,12 +26,20 @@ of your built assets, you will want to explicitly reference the package's `index
import {MDCFoo, MDCFooFoundation} from '@material/foo/index';
```

Certain build tools will often pick up on the use of `package.json`'s `module` property, which points to the ES2015+ `index.js` source code. If you are using [Webpack](https://webpack.js.org/) or [Rollup](https://rollupjs.org/guide/en), then you will not need to reference the `index.js` file, and can continue to use the `@material/foo` import paths.
Copy link
Contributor

Choose a reason for hiding this comment

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

It looks like this comment hasn't been applied.

Furthermore, index.js isn't ES2015+ source anymore. It's ES5 source in an ES module.

@mdc-web-bot
Copy link
Collaborator

All 624 screenshot tests passed for commit 42e349b vs. feat/typescript! 💯🎉

@mdc-web-bot
Copy link
Collaborator

All 624 screenshot tests passed for commit 160fd92 vs. feat/typescript! 💯🎉

@mdc-web-bot
Copy link
Collaborator

All 624 screenshot tests passed for commit 0790186 vs. feat/typescript! 💯🎉

@mdc-web-bot
Copy link
Collaborator

All 624 screenshot tests passed for commit 618d556 vs. feat/typescript! 💯🎉

@mdc-web-bot
Copy link
Collaborator

All 624 screenshot tests passed for commit 72ce98f vs. feat/typescript! 💯🎉

@moog16 moog16 mentioned this pull request Mar 1, 2019
@moog16 moog16 closed this Mar 1, 2019
@mdc-web-bot
Copy link
Collaborator

All 624 screenshot tests passed for commit 7cd0007 vs. feat/typescript! 💯🎉

@moog16 moog16 deleted the chore/replace-js-for-ts branch March 1, 2019 22:41
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants