phetsims / tasks

General tasks for PhET that will be tracked on Github
MIT License
5 stars 2 forks source link

Investigate use of Typescript #987

Closed Denz1994 closed 3 years ago

Denz1994 commented 5 years ago

From Dev Meeting: 02/14/19

While reviewing https://github.com/phetsims/dot/issues/84, it became apparent that the loosely typed nature of javascript makes refactors more difficult, as seen by the commits made by @samreid here. @mbarlow12 was advocating that Typescript may be a useful alternative.

Some benefits include typechecking, target directories for compiling, and a javascript in and out approach.

Potentially, DOTcan be prototyped for experimentation. Assigning to @mbarlow12 for follow up.

mbarlow12 commented 5 years ago

Here's a rough plan of attack:

My biggest question/concern is how we're handling module dependencies. If I recall correctly, this was a major sticking point during the ES6 discussion regarding import and export statements as well as properly accessing common code from a sim/repo .ts file (plenty of info about this out on the internets and most make it seem like incremental transition is quite possible).

samreid commented 5 years ago

My biggest question/concern is how we're handling module dependencies.

We would have the same barrier to using Flow for types.

ariel-phet commented 5 years ago

Is this something we want to continue to pursue?

samreid commented 5 years ago

Some thoughts on the topic, not necessarily connected, complete or in order of importance:

jbphet commented 5 years ago

@jonathanolson mentioned another concern in the 6/6/2019 dev meeting:

jbphet commented 5 years ago

This was discussed in today's dev meeting, we did a poll, and ultimately decided to look at this and related language improvements at some point in the future.

ariel-phet commented 5 years ago

Gave myself a calendar reminder for Nov 4 2019. Unassigning for now

samreid commented 4 years ago

I followed these instructions to tell WebStorm to check JS code using the Typescript type checker: https://blog.jetbrains.com/webstorm/2019/09/using-typescript-to-check-your-javascript-code/

The results looked something like this for main/gas-properties/js/diffusion/view/DiffusionSettingsNode.js

image

samreid commented 4 years ago

To elaborate on the previous comment, TypeScript has a feature to type check JS files described here: https://www.typescriptlang.org/docs/handbook/type-checking-javascript-files.html

image

It may be a way for us to leverage typescript type checking and our existing JSDoc without switching languages.

samreid commented 4 years ago

I saw this article about why one company decided to move from Typescript to Javascript: https://docs.google.com/document/d/1_WvwHl7BXUPmoiSeD8G83JmS8ypsTPqed4Btkqkn_-4/preview?pru=AAABcr1aFbQ*c7R6Bt9IKDZaBJdD7_Ie0g

Some highlights:

samreid commented 3 years ago

We discussed typescript again today in the context of https://github.com/phetsims/axon/issues/343. Would be nice to have interfaces and not rely on TypeDef/documentation.

Typescript also came up in the context of https://github.com/phetsims/phet-info/issues/143 where we discussed mixins and traits.

I reaffirmed that it is very efficient to run code in the browser without a compile step. And that the widespread usage of JS is appealing. Would TS make it difficult or impossible to optimize certain code bottlenecks? How much effort would we have to invest to port everything enough to get the benefits of TS?

How would typescript deal with our established options pattern? See https://rclayton.silvrback.com/easy-class-api-options-with-typescript-and-joi

samreid commented 3 years ago

We keep identifying problem after problem that could potentially be solved by typescript. I'd like to open this for a short developer meeting conversation to ask the following questions:

(1) Can we rule it out as a possibility? For instance, here are some potentially blocking technical problems:

or any other potentially blocking problem. I suspect that many practical problems could be solved, worked around or avoided, but I recommend a developer meeting to gauge developer interest and preference around practical problems like:

(2) If we cannot rule it out as a possibility, let's discuss a potential Q3 investigation and Q4 migration. If we want to move forward, this issue would become an epic "investigation" issue and another issue would be for the migration. I would like to be involved with both the investigation and migration if we agree to proceed.

Quick list of what we stand to benefit:

Notes for investigation:

samreid commented 3 years ago

I wanted to understand better how typescript would deal with options, and I tried creating an example project.

type StormOptions = {
    darkSkies: boolean,
    prettyWindy: boolean
};

type TornadoOptions = {
    canLiftAHouse: boolean,
    name: string
} & StormOptions;

When I pressed for code completion in the options, typescript mentioned all of the possible allowed keys and where they were defined.

If I try providing an option that doesn’t exist, typescript gives me a heads-up:

I am surprised and interested that types can be applied to the options and not just the class types. I also tested that this allows us to rename key names across occurrences and it seemed to work great.

Patch with this example:

```diff Index: tsunami/src/main.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/tsunami/src/main.ts b/tsunami/src/main.ts new file mode 100644 --- /dev/null (date 1619056212792) +++ b/tsunami/src/main.ts (date 1619056212792) @@ -0,0 +1,26 @@ +import Storm from "./Storm"; +import Tornado from "./Tornado"; + +const user = { + name: "Hayes", + id: 0, +}; + +console.log( user ); + +// user.name = 7; + + +const storm = new Storm( { + darkSkies: true, + monkeyAttack: false, + } ); +storm.startWind(); +storm.endWind(); + +const secondStorm = new Storm(); +storm.setNextStorm( secondStorm ); + +const tornado = new Tornado( { + darkSkies: false + } ); \ No newline at end of file Index: tsunami/src/StormOptions.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/tsunami/src/StormOptions.ts b/tsunami/src/StormOptions.ts new file mode 100644 --- /dev/null (date 1619042866022) +++ b/tsunami/src/StormOptions.ts (date 1619042866022) @@ -0,0 +1,8 @@ +// TODO: This should be part of Storm.ts + +type StormOptions = { + darkSkies: boolean, + prettyWindy: boolean +}; + +export default StormOptions; \ No newline at end of file Index: tsunami/src/Tornado.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/tsunami/src/Tornado.ts b/tsunami/src/Tornado.ts new file mode 100644 --- /dev/null (date 1619030578442) +++ b/tsunami/src/Tornado.ts (date 1619030578442) @@ -0,0 +1,18 @@ +import Storm from "./Storm"; +import StormOptions from "./StormOptions"; + +type Blizzard = { + snow: boolean +}; +type TornadoOptions = { + canLiftAHouse: boolean, + name: string +} & StormOptions & Blizzard; + +class Tornado extends Storm { + constructor( options?: Partial ) { + super(); + } +} + +export default Tornado \ No newline at end of file Index: tsunami/src/Storm.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/tsunami/src/Storm.ts b/tsunami/src/Storm.ts new file mode 100644 --- /dev/null (date 1619043810694) +++ b/tsunami/src/Storm.ts (date 1619043810694) @@ -0,0 +1,36 @@ +// type StormOptions = { +// darkSkies: boolean, +// prettyWindy: boolean +// }; + +import StormOptions from "./StormOptions"; + +class Storm { + + public darkSkies: boolean; + public prettyWindy: boolean; + isCurrentlyWindy: boolean; + nextStorm: Storm;p + // static StormOptions: any; + + constructor( options?: Partial ) { + this.darkSkies = options.darkSkies; + this.prettyWindy = options.prettyWindy; + } + + public startWind() { + this.isCurrentlyWindy = true; + } + + endWind() { + this.isCurrentlyWindy = false; + } + + public setNextStorm( storm: Storm ) { + this.nextStorm = storm; + } +} + +// Storm.StormOptions = StormOptions; +// +export default Storm; \ No newline at end of file ```
jonathanolson commented 3 years ago

That's impressive!

samreid commented 3 years ago

After the above commit, example-sim is written in typescript (in a branch). The migration took around an hour, and that includes the time to add some auxiliary types, which I'll put in a patch below:

```diff Index: tandem/js/PhetioObject.d.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/tandem/js/PhetioObject.d.ts b/tandem/js/PhetioObject.d.ts new file mode 100644 --- /dev/null (date 1619324109807) +++ b/tandem/js/PhetioObject.d.ts (date 1619324109807) @@ -0,0 +1,7 @@ +import Tandem from "./Tandem"; + +export type PhetioObjectOptions = { + tandem: Tandem; + phetioDocumentation: string; +}; +export default class PhetioObject {} \ No newline at end of file Index: scenery/js/nodes/Image.d.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/scenery/js/nodes/Image.d.ts b/scenery/js/nodes/Image.d.ts new file mode 100644 --- /dev/null (date 1619323488187) +++ b/scenery/js/nodes/Image.d.ts (date 1619323488187) @@ -0,0 +1,10 @@ +import Node from './Node'; +import {NodeOptions} from './Node'; + +export type ImageOptions = { + imageName: string +} & NodeOptions; + +export default class Image extends Node { + constructor( any, options?: Partial ); +} \ No newline at end of file Index: scenery/js/nodes/Node.d.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/scenery/js/nodes/Node.d.ts b/scenery/js/nodes/Node.d.ts new file mode 100644 --- /dev/null (date 1619324469561) +++ b/scenery/js/nodes/Node.d.ts (date 1619324469561) @@ -0,0 +1,31 @@ +import Vector2 from "../../../dot/js/Vector2"; +import {PhetioObjectOptions} from "../../../tandem/js/PhetioObject.js"; + +export type NodeOptions = { + cursor: string; + maxWidth: number; + centerX: number; + centerY: number; + x: number; + y: number; + rotation: number; +} & PhetioObjectOptions; + +export default class Node { + constructor( options?: Partial ); + + addChild( node: Node ); + + scale( s: number ); + scale( sx: number, sy: number ); + + set translation( t: Vector2 ); + + set rotation( r: number ); + + addInputListener( listener: any ): void; + + get height(): number; + + get width(): number; +} \ No newline at end of file Index: phet-core/js/Enumeration.d.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/phet-core/js/Enumeration.d.ts b/phet-core/js/Enumeration.d.ts new file mode 100644 --- /dev/null (date 1619298736882) +++ b/phet-core/js/Enumeration.d.ts (date 1619298736882) @@ -0,0 +1,7 @@ +// export default interface Enumeration { +// UNDEFER: string, +// +// VALUES: string, +// phetioDocumentation, +// keys: string +// } \ No newline at end of file Index: phet-core/js/Namespace.d.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/phet-core/js/Namespace.d.ts b/phet-core/js/Namespace.d.ts new file mode 100644 --- /dev/null (date 1619321911468) +++ b/phet-core/js/Namespace.d.ts (date 1619321911468) @@ -0,0 +1,9 @@ +export default class Namespace { + constructor( string ); + + register( string, any ): void; + + moduloBetweenUp: any; + toRadians: any; + toDegrees: any; +} \ No newline at end of file Index: axon/js/validate.d.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/axon/js/validate.d.ts b/axon/js/validate.d.ts new file mode 100644 --- /dev/null (date 1619398310275) +++ b/axon/js/validate.d.ts (date 1619398310275) @@ -0,0 +1,2 @@ +export default function validate(x:any,validator:any): void; +export default function validate(x:any,parameter:any,text:string,options:any): void; \ No newline at end of file ```

The main is launched via: http://localhost/main/chipper/runts.html?sim=example-sim

runts.html is in chipper:

```html acid-base-solutions ```

and here's tsconfig.json:

```json { "include": [ "../../brand/js/**/*", "../../brand/adapted-from-phet/**/*", "types/phet.d.ts", "../../example-sim/**/*" // "../../acid-base-solutions/**/*", // "../../area-builder/**/*", // "../../area-model-algebra/**/*", // "../../area-model-decimals/**/*", // "../../area-model-introduction/**/*", // "../../area-model-multiplication/**/*", // "../../arithmetic/**/*", // "../../atomic-interactions/**/*", // "../../balancing-act/**/*", // "../../balancing-chemical-equations/**/*", // "../../balloons-and-static-electricity/**/*", // "../../bamboo/**/*", // "../../beers-law-lab/**/*", // "../../bending-light/**/*", // "../../blackbody-spectrum/**/*", // "../../blast/**/*", // "../../build-a-fraction/**/*", // "../../build-a-molecule/**/*", // "../../build-an-atom/**/*", // "../../bumper/**/*", // "../../buoyancy/**/*", // "../../calculus-grapher/**/*", // "../../capacitor-lab-basics/**/*", // "../../chains/**/*", // "../../charges-and-fields/**/*", // "../../circuit-construction-kit-ac/**/*", // "../../circuit-construction-kit-ac-virtual-lab/**/*", // "../../circuit-construction-kit-black-box-study/**/*", // "../../circuit-construction-kit-common/**/*", // "../../circuit-construction-kit-dc/**/*", // "../../circuit-construction-kit-dc-virtual-lab/**/*", // "../../collision-lab/**/*", // "../../color-vision/**/*", // "../../concentration/**/*", // "../../coulombs-law/**/*", // "../../curve-fitting/**/*", // "../../density/**/*", // "../../diffusion/**/*", // "../../eating-exercise-and-energy/**/*", // "../../energy-forms-and-changes/**/*", // "../../energy-skate-park/**/*", // "../../energy-skate-park-basics/**/*", // "../../equality-explorer/**/*", // "../../equality-explorer-basics/**/*", // "../../equality-explorer-two-variables/**/*", // "../../estimation/**/*", // "../../example-sim/**/*", // "../../expression-exchange/**/*", // "../../faradays-law/**/*", // "../../fluid-pressure-and-flow/**/*", // "../../forces-and-motion-basics/**/*", // "../../fourier-making-waves/**/*", // "../../fraction-comparison/**/*", // "../../fraction-matcher/**/*", // "../../fractions-equality/**/*", // "../../fractions-intro/**/*", // "../../fractions-mixed-numbers/**/*", // "../../friction/**/*", // "../../function-builder/**/*", // "../../function-builder-basics/**/*", // "../../gas-properties/**/*", // "../../gases-intro/**/*", // "../../geometric-optics/**/*", // "../../gene-expression-essentials/**/*", // "../../graphing-lines/**/*", // "../../graphing-quadratics/**/*", // "../../graphing-slope-intercept/**/*", // "../../gravity-and-orbits/**/*", // "../../gravity-force-lab/**/*", // "../../gravity-force-lab-basics/**/*", // "../../greenhouse-effect/**/*", // "../../griddle/**/*", // "../../hookes-law/**/*", // "../../interaction-dashboard/**/*", // "../../isotopes-and-atomic-mass/**/*", // "../../john-travoltage/**/*", // "../../joist/**/*", // "../../least-squares-regression/**/*", // "../../make-a-ten/**/*", // "../../masses-and-springs/**/*", // "../../masses-and-springs-basics/**/*", // "../../models-of-the-hydrogen-atom/**/*", // "../../molarity/**/*", // "../../molecule-polarity/**/*", // "../../molecule-shapes/**/*", // "../../molecule-shapes-basics/**/*", // "../../molecules-and-light/**/*", // "../../my-solar-system/**/*", // "../../natural-selection/**/*", // "../../neuron/**/*", // "../../normal-modes/**/*", // "../../number-line-distance/**/*", // "../../number-line-integers/**/*", // "../../number-line-operations/**/*", // "../../number-play/**/*", // "../../ohms-law/**/*", // "../../optics-lab/**/*", // "../../pendulum-lab/**/*", // "../../ph-scale/**/*", // "../../ph-scale-basics/**/*", // "../../phet-io-test-sim/**/*", // "../../plinko-probability/**/*", // "../../projectile-motion/**/*", // "../../proportion-playground/**/*", // "../../ratio-and-proportion/**/*", // "../../reactants-products-and-leftovers/**/*", // "../../resistance-in-a-wire/**/*", // "../../rutherford-scattering/**/*", // "../../scenery-phet/**/*", // "../../simula-rasa/**/*", // "../../states-of-matter/**/*", // "../../states-of-matter-basics/**/*", // "../../sun/**/*", // "../../tambo/**/*", // "../../tappi/**/*", // "../../trig-tour/**/*", // "../../twixt/**/*", // "../../under-pressure/**/*", // "../../unit-rates/**/*", // "../../vector-addition/**/*", // "../../vector-addition-equations/**/*", // "../../vegas/**/*", // "../../vibe/**/*", // "../../wave-interference/**/*", // "../../wave-on-a-string/**/*", // "../../waves-intro/**/*", // "../../wilder/**/*", // "../../xray-diffraction/**/*" ], "compilerOptions": { "module": "es6", "target": "es6", "sourceMap": true, "outDir": "build/ts/", "allowJs": true, "noEmitOnError": false, "strict": false, "incremental": true, "checkJs": false, "types": [], "lib": [ "es6", "dom", "es2017" ] }, "exclude": [ "../node_modules/**/*" ] } ```

And some ambient types:

```js /* eslint-disable */ // import Tandem from "../../../tandem/js/Tandem"; declare var assert: any; declare var phet: any; declare var phetio: any; declare var _: any; declare var assertSlow: any; declare var sceneryLog: any; declare var scenery: any; // declare var Enumeration: any; declare var QUnit: any; declare var module: any declare interface PhetioObject { greeting: string; duration?: number; color?: string; // tandem: Tandem; } declare interface Enumeration { NOTIFY: string; IDENTITY: string; } ```

I've kept most of this out of master to avoid unintentionally disrupting other developers' environments. Webstorm prefers to navigate to d.ts files if they exist.

samreid commented 3 years ago

Notes from discussion with @jonathanolson:

Really excited, seems promising--typescript has many features that we have been creating on our own.

Questions about Features:

Requires Investigation:

samreid commented 3 years ago

Today at dev meeting, I demonstrated the following features using example-sim's typescript branch:

The development team generally seemed interested and excited about these features. We also briefly discussed:

JB: Traits and mixins JO: Generic type support, mixins MK: Interfaces

MK: How to deal with shared files between chipper and perennial, like SimVersion.
SR: Right now that is a preload, but would work better as a module.

JB: How fast is the turnaround time when compiling a larger simulation? SR: I tried building a compilation unit that included all sims, and it worked OK and quickly, but slowed down if changing the interface of a central file like Node. Some ideas for improving that are using typescript project references to break it down into smaller units, or using a pure transpile step for quick iteration.

JO: Make sure it works well with the build process, maintenance releases, etc. So there will be a transition period.

MK: How could we revise the string plugin? SR: JSON files can be imported natively by typescript.

MK: Is typescript well maintained? How does the transpiled output look?

MK: Should we have something like wilder, but for typescript. Try the fun features in a single place.

Designers and others may have local copies checked out, which would need a build step. This will also be a problem for CT, phet-test, etc.

SR: I'll continue to investigate at low priority in the background. When I have answers to more of these foundational questions, then we can come up with a plan and proposal to discuss with Kathy.

samreid commented 3 years ago

SR: I'll continue to investigate at low priority in the background. When I have answers to more of these foundational questions, then we can come up with a plan and proposal to discuss with Kathy.

Maybe a better plan would be to discuss with Kathy sooner rather than later, and elevate investigations to medium priority--something that can be done when my Q2 goals are complete or awaiting feedback.

Re-labeling for dev meeting to touch base and confirm how to proceed.

chrisklus commented 3 years ago

From 5/6/21 dev meeting:

SR: How do devs feel about moving forward with investigations at medium priority?

All devs are on board with proceeding! (UPDATE from @samreid: note that @pixelzoom and @jbphet are absent today)

MP: what scope would this cover?

SR: Level 1: sim-specific code Level 2: sim common code Level 3: node code Level 4: PhET-iO wrappers

zepumph commented 3 years ago

I'm really excited about this, and look forward to future investigation. I feel like it is unlikely that any con we find outweighs all the potential benefits.

I also noticed that we can use TypeScript in node pretty easily without a compile step, as we can treat it as its own language. This is exciting! See https://nodejs.dev/learn/nodejs-with-typescript

pixelzoom commented 3 years ago

@samreid said:

Maybe a better plan would be to discuss with Kathy sooner rather than later, and elevate investigations to medium priority--something that can be done when my Q2 goals are complete or awaiting feedback.

I think it's best to discuss with project management sooner. From what I've heard, there will be long-term benefits. But there will also be significant short-term costs (learning curve, converting code, establishing new patterns, build tools, etc.) that will need to be balanced with other project goals. Without incurring some of the short-term costs, I don't have the experience to quantify the costs or benefits.

All devs are on board with proceeding! (UPDATE from @samreid: note that @pixelzoom and @jbphet are absent today)

I'll be happy to use TypeScript, if that's what PhET decides.

samreid commented 3 years ago

Today, typescript came up while @zepumph and I were discussing https://github.com/phetsims/phet-io/issues/1763 with @kathy-phet. We gave a brief description and overview of the language, described how it has been proposed as a solution to numerous different PhET problems, and how other companies such as Slack, Airbnb and Google have reported significant advantages when they converted their codebases (referring to the "Migration Stories" listed on https://www.typescriptlang.org/). I reported sharing initial ideas with the development team and that the dev team said it sounds promising and recommends further investigation. @kathy-phet agreed it would be good to move forward with the investigation, starting after Q2 goals are winding down. I added a new row to the PhET Project Overview document.

samreid commented 3 years ago

From discussion with @pixelzoom:

CM: Can webstorm do the compilation for us? Instead of tsc --watch.

CM: A colleague pointed out that in the debugger, you can end up in the additional compiled code, not your code. Got around that via breakpoint in his code. Going from "parallel universe" back to your code as you debug.

CM: 3rd parties will need to know typescript. That may be one more entry barrier for 3rd parties. SR: On the other hand, this could help newcomers navigate and understand our code. CM: Hiring a new developer, typescript experience wouldn't be essential. For example, [...] got up to speed quickly without much (any?) js experience. So TS may not be too much of a barrier. I'd rather have a good learner than someone with a lot of typescript experience but keeps making the same mistakes over and over. TS is lower on the list of desirable attributes for a new hire.

SR: We could follow this order for migration:

  1. sim code
  2. sim common code
  3. node
  4. phet-io wrappers (exclude the starting template--maybe that should be in javascript)

CM: Should this be focused for internal code? Public code APIs would remain in JS.

SR: How will we estimate the effort to port everything? Common code? Sims? CM: If it really has advantages, we would probably want to convert sooner rather than later. I prefer being proactive about this kind of thing. If it seems like a win in one sim... But not wanting to change working code.

CM: Before we start coding, determine other conventions we should change, etc. Should we still use JSDoc the same way? Like function parameters, etc. ColorDef => interface or abstract class Mixins Enum. Do we want enum or type = 'a' | 'b'. Could we get rid of EnumerationIO if we just use strings? So studio could show radio buttons and not a text field. Make sure we aren't missing something that would sabotage phet-io.

CM: Webstorm doesn't know that NumberProperty is Property. Make sure this is handled well.

SR: How long will we be happy using both TS/JS in our codebase?

SR: Are there any milestones or sweet spots that would be good to hit for migration? Before a new hire? CM: If that's within 6 months, JS will probably be very important. Maybe discuss whether they are willing to typescript. Before SR starts E&E CM: It's nice to do the investigation with something concrete. Make sure the sim also gets phet-io instrumentation to make sure it's a good fit.

CM: Fourier is supposed to be done by Sept. Feature complete end of June. I wouldn't convert it before publication. Other features were not scheduled, but creeping in. May have a new sim in Aug/Sept. SR: Maybe only use typescript on sims until fourier is published, so it isn't destabilized? CM: As we get closer to feature complete or QA testing, sooner would be better.

CM: I don't think I need to be sold on the idea that TS is a better language. There will be an initial learning curve, but I expect it will pay dividends down the road.

zepumph commented 3 years ago

Oops

zepumph commented 3 years ago

I didn't know if we were going to have to totally convert away from eslint, it looks like there is a chance we wouldn't necessarily have to (https://khalilstemmler.com/blogs/typescript/eslint-for-typescript/), but perhaps tslint will make more sense to use because we will be writing in a different language.

samreid commented 3 years ago

Here's a patch that adds 2 modes of typescript builds to chipper:

(1) grunt tsc runs the typescript compiler on a project and outputs the result to chipper/dist. The HTML file points to chipper/dist. This would be used in place of "unbuilt" for fast iteration. Using package.json would enforce the same compiler verison. We would need this to run on phettest, bayes, fresh checkouts, etc. This is also the build that IDEs such as WebStorm would use, since it integrates with the tsconfig.json. May be able to make iteration even faster with a transpile-only mode, but it is already kind of fast (I split it into 2 modules). (2) grunt supports typescript via the ts-loader. I tested by converting a few example-sim files to typescript. It works OK but leaves some things to be desired.

```diff Index: main/chipper/tsconfig.json IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/tsconfig.json b/main/chipper/tsconfig.json new file mode 100644 --- /dev/null (date 1622759164753) +++ b/main/chipper/tsconfig.json (date 1622759164753) @@ -0,0 +1,105 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ + "module": "ES2020", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + "allowJs": true, /* Allow javascript files to be compiled. */ + "checkJs": false, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./dist", /* Redirect output structure to the directory. */ + "rootDir": "../", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": false, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + }, + "include": [ + "./phet-types.d.tstambo/js/demo/testing/view/TestingScreenView.js", + "../chipper/phet-types.d.ts", + "../chipper/js/getStringModule.js", + "../chipper/js/sim-tests/qunitStart.js", + "../axon/js/**/*", + "../brand/js/**/*", + "../brand/phet/js/Brand.js", + "../brand/phet/images/**/*", + "../dot/js/**/*", + "../joist/js/**/*", + "../joist/images/**/*", + "../joist/sounds/**/*", + "../kite/js/**/*", + "../phet-core/js/**/*", + "../phet-io/js/**/*", + "../phetcommon/js/**/*", + "../scenery/js/**/*", + "../sherpa/js/**/*", + "../sherpa/lib/lodash-4.17.4.min.js", + "../sun/js/**/*", + "../scenery-phet/js/**/*", + "../scenery-phet/images/**/*", + "../scenery-phet/mipmaps/**/*", + "../scenery-phet/sounds/**/*", + "../sherpa/lib/game-up-camera-1.0.0.js", + "../tambo/js/**/*", + "../tambo/sounds/**/*", + "../tambo/images/**/*", + "../tandem/js/**/*", + "../twixt/js/**/*", + "../utterance-queue/js/**/*" + ] +} \ No newline at end of file Index: main/chipper/js/grunt/Gruntfile.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/js/grunt/Gruntfile.js b/main/chipper/js/grunt/Gruntfile.js --- a/main/chipper/js/grunt/Gruntfile.js (revision cb0d287b7dec072d5014a82a886b24b9a8369310) +++ b/main/chipper/js/grunt/Gruntfile.js (date 1622764026748) @@ -13,6 +13,7 @@ const buildRunnable = require( './buildRunnable' ); const buildStandalone = require( './buildStandalone' ); const commitsSince = require( './commitsSince' ); +const compileTypescript = require( './compileTypescript' ); const phetioCompareAPISets = require( '../phet-io/phetioCompareAPISets' ); const generateA11yViewHTML = require( './generateA11yViewHTML' ); const generateDevelopmentColorsHTML = require( './generateDevelopmentColorsHTML' ); @@ -120,6 +121,13 @@ } grunt.file.mkdir( buildDirectory ); } ) ); + + grunt.registerTask( 'tsc', + 'Runs the typescript compiler', + wrapTask( async () => { + const buildDirectory = `../${repo}/tsconfig.json`; + compileTypescript( buildDirectory ); + } ) ); grunt.registerTask( 'build-images', 'Build images only', Index: main/example-sim/example-sim_en.html IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/example-sim/example-sim_en.html b/main/example-sim/example-sim_en.html --- a/main/example-sim/example-sim_en.html (revision 86ca81c92a74f01ff427752a4299086d34828050) +++ b/main/example-sim/example-sim_en.html (date 1622758574915) @@ -124,7 +124,7 @@ // Module loading in compilation-free (development) mode will be kicked off once strings are loaded. // This is done in load-unbuilt-strings.js - window.phet.chipper.loadModules = () => loadURL( 'js/example-sim-main.js', 'module' ); + window.phet.chipper.loadModules = () => loadURL( '../chipper/dist/example-sim/js/example-sim-main.js', 'module' ); \ No newline at end of file Index: main/chipper/js/grunt/webpackBuild.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/js/grunt/webpackBuild.js b/main/chipper/js/grunt/webpackBuild.js --- a/main/chipper/js/grunt/webpackBuild.js (revision cb0d287b7dec072d5014a82a886b24b9a8369310) +++ b/main/chipper/js/grunt/webpackBuild.js (date 1622777446692) @@ -35,14 +35,17 @@ * @returns {Array.} */ const getRelativeModules = modules => { - for ( let i = 0; i < modules[ 0 ].length; i++ ) { - for ( const usedModule of modules ) { - if ( usedModule[ i ] !== modules[ 0 ][ i ] ) { - return modules.map( module => module.slice( i ) ); - } - } - } - throw new Error( 'modules are all the same?' ); + console.log( modules ); + const k = '/Users/samreid/apache-document-root/main/'; + return modules.filter( m => m.length >= k.length ).map( m => m.substring( k.length ) ); + // for ( let i = 0; i < modules[ 0 ].length; i++ ) { + // for ( const usedModule of modules ) { + // if ( usedModule[ i ] !== modules[ 0 ][ i ] ) { + // return modules.map( module => module.slice( i ) ); + // } + // } + // } + // throw new Error( 'modules are all the same?' ); }; /** @@ -91,6 +94,26 @@ path: path.resolve( __dirname, `../../${ChipperConstants.BUILD_DIR}` ), filename: `${repo}.js` }, + module: { + rules: [ + { + test: /\.ts$/, use: [ + { + loader: 'ts-loader', + options: { + // transpileOnly: true + // configFile: path.resolve( __dirname, `../${repo}/tsconfig.js` ) + configFile: '/Users/samreid/apache-document-root/main/example-sim/tsconfig.json' + } + } + ] + } + ] + }, + + resolve: { + extensions: [ '.ts', '.js' ] + }, // {Array.} plugins: Index: main/chipper/js/grunt/compileTypescript.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/js/grunt/compileTypescript.js b/main/chipper/js/grunt/compileTypescript.js new file mode 100644 --- /dev/null (date 1622765663223) +++ b/main/chipper/js/grunt/compileTypescript.js (date 1622765663223) @@ -0,0 +1,54 @@ +// Copyright 2021, University of Colorado Boulder +'use strict'; + +/** + * Adapted from https://github.com/Microsoft/TypeScript/issues/6387 + * and + * https://stackoverflow.com/questions/57333825/can-you-pull-in-excludes-includes-options-in-typescript-compiler-api + * + * @author Sam Reid (PhET Interactive Simulations) + */ + +const typescript = require( 'typescript' ); +const fs = require( 'fs' ); +const path = require( 'path' ); + +function reportErrors( diagnostics ) { + diagnostics.forEach( diagnostic => { + let message = 'Error'; + if ( diagnostic.file ) { + const where = diagnostic.file.getLineAndCharacterOfPosition( diagnostic.start ); + message += ` ${diagnostic.file.fileName} ${where.line}, ${where.character}`; + } + message += `: ${typescript.flattenDiagnosticMessageText( diagnostic.messageText, '\n' )}`; + console.log( message ); + } ); +} + +module.exports = function( configFileName ) { + + // Get filenames to compile from the config + // https://stackoverflow.com/questions/57333825/can-you-pull-in-excludes-includes-options-in-typescript-compiler-api + const configFile = typescript.readJsonConfigFile( configFileName, typescript.sys.readFile ); + const { fileNames } = typescript.parseJsonSourceFileConfigFileContent( + configFile, + typescript.sys, + './' + ); + + // Extract configuration from config file + // TODO: use the same config as above? See https://github.com/phetsims/tasks/issues/987 + const configFileString = fs.readFileSync( configFileName ).toString(); + const configFileJSON = typescript.parseConfigFileTextToJson( configFileName, configFileString ); + const config = typescript.parseJsonConfigFileContent( configFileJSON.config, typescript.sys, path.dirname( configFileName ) ); + if ( config.errors.length > 0 ) { + reportErrors( config.errors ); + } + + // Compile + const program = typescript.createProgram( fileNames, config.options ); + const emitResult = program.emit(); + + // Report errors + reportErrors( typescript.getPreEmitDiagnostics( program ).concat( emitResult.diagnostics ) ); +}; \ No newline at end of file Index: main/example-sim/package.json IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/example-sim/package.json b/main/example-sim/package.json --- a/main/example-sim/package.json (revision 86ca81c92a74f01ff427752a4299086d34828050) +++ b/main/example-sim/package.json (date 1622774167744) @@ -7,7 +7,9 @@ "url": "https://github.com/phetsims/example-sim.git" }, "devDependencies": { - "grunt": "~1.4.0" + "grunt": "~1.4.0", + "ts-loader": "^9.2.2", + "webpack": "^5.38.1" }, "phet": { "requirejsNamespace": "EXAMPLE_SIM", @@ -23,4 +25,4 @@ "eslintConfig": { "extends": "../chipper/eslint/sim_eslintrc.js" } -} \ No newline at end of file +} Index: main/chipper/js/grunt/getStringMap.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/js/grunt/getStringMap.js b/main/chipper/js/grunt/getStringMap.js --- a/main/chipper/js/grunt/getStringMap.js (revision cb0d287b7dec072d5014a82a886b24b9a8369310) +++ b/main/chipper/js/grunt/getStringMap.js (date 1622776428492) @@ -100,7 +100,17 @@ }; // Load the file contents of every single JS module that used any strings - const usedFileContents = usedModules.map( usedModule => fs.readFileSync( `../${usedModule}`, 'utf-8' ) ); + const usedFileContents = usedModules.map( usedModule => { + console.log( usedModule ); + try { + return fs.readFileSync( `../${usedModule}`, 'utf-8' ); + } + catch( e ) { + + // maybe a directory + return ''; + } + } ); // Compute which repositories contain one more more used strings (since we'll need to load string files for those // repositories). Index: main/chipper/.gitignore IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/.gitignore b/main/chipper/.gitignore --- a/main/chipper/.gitignore (revision cb0d287b7dec072d5014a82a886b24b9a8369310) +++ b/main/chipper/.gitignore (date 1622761203816) @@ -9,3 +9,4 @@ *.sublime-workspace package-lock.json build +dist \ No newline at end of file Index: main/chipper/phet-types.d.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/phet-types.d.ts b/main/chipper/phet-types.d.ts new file mode 100644 --- /dev/null (date 1622757229065) +++ b/main/chipper/phet-types.d.ts (date 1622757229065) @@ -0,0 +1,27 @@ +/* eslint-disable */ + +// import Tandem from "../../../tandem/js/Tandem"; + +declare var assert: any; +declare var phet: any; +declare var phetio: any; +declare var _: any; +declare var assertSlow: any; +declare var sceneryLog: any; +declare var scenery: any; +// declare var Enumeration: any; +declare var QUnit: any; + +declare var module: any + +declare interface PhetioObject { + greeting: string; + duration?: number; + color?: string; + // tandem: Tandem; +} + +declare interface Enumeration { + NOTIFY: string; + IDENTITY: string; +} \ No newline at end of file Index: main/chipper/js/grunt/buildRunnable.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/js/grunt/buildRunnable.js b/main/chipper/js/grunt/buildRunnable.js --- a/main/chipper/js/grunt/buildRunnable.js (revision cb0d287b7dec072d5014a82a886b24b9a8369310) +++ b/main/chipper/js/grunt/buildRunnable.js (date 1622776612930) @@ -126,8 +126,15 @@ const dependencies = await getDependencies( repo ); webpackResult.usedModules.forEach( moduleDependency => { - const moduleRepo = moduleDependency.slice( 0, moduleDependency.indexOf( path.sep ) ); - assert( Object.keys( dependencies ).includes( moduleRepo ), `repo ${moduleRepo} missing from package.json's phetLibs for ${moduleDependency}` ); + console.log( moduleDependency ); + const index = moduleDependency.indexOf( path.sep ); + if ( index >= 0 ) { + const moduleRepo = moduleDependency.slice( 0, index ); + console.log( moduleRepo ); + if ( moduleRepo !== 'modules' ) { + assert( Object.keys( dependencies ).includes( moduleRepo ), `repo ${moduleRepo} missing from package.json's phetLibs for ${moduleDependency}` ); + } + } } ); const version = packageObject.version; // Include the one-off name in the version Index: main/chipper/eslint/.eslintignore IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/eslint/.eslintignore b/main/chipper/eslint/.eslintignore --- a/main/chipper/eslint/.eslintignore (revision cb0d287b7dec072d5014a82a886b24b9a8369310) +++ b/main/chipper/eslint/.eslintignore (date 1622763450377) @@ -12,4 +12,5 @@ ../phet-io-website/root/assets/js/QueryStringMachine_1.0.js ../phet-io-website/root/assets/js/QueryStringMachine_2.0.js ../phet-io-website/root/assets/js/phet-io-ga.js -../installer-builder/temp/** \ No newline at end of file +../installer-builder/temp/** +../chipper/dist \ No newline at end of file Index: main/example-sim/tsconfig.json IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/example-sim/tsconfig.json b/main/example-sim/tsconfig.json new file mode 100644 --- /dev/null (date 1622775160852) +++ b/main/example-sim/tsconfig.json (date 1622775160852) @@ -0,0 +1,82 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ + "module": "ES2020", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + "allowJs": true, /* Allow javascript files to be compiled. */ + "checkJs": false, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "../chipper/dist", /* Redirect output structure to the directory. */ + "rootDir": "../", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ +// "composite": false, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + }, + "references": [ + { + "path": "../chipper" + } + ], + "files": ["js/example-sim-main.js"], +// "include": [ +// "js/", +// "images/" +// ] +} \ No newline at end of file Index: main/example-sim/js/example/ExampleScreen.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/example-sim/js/example/ExampleScreen.js b/main/example-sim/js/example/ExampleScreen.js --- a/main/example-sim/js/example/ExampleScreen.js (revision 86ca81c92a74f01ff427752a4299086d34828050) +++ b/main/example-sim/js/example/ExampleScreen.js (date 1622773193811) @@ -10,7 +10,7 @@ import Screen from '../../../joist/js/Screen.js'; import exampleSim from '../exampleSim.js'; import ExampleModel from './model/ExampleModel.js'; -import ExampleScreenView from './view/ExampleScreenView.js'; +import ExampleScreenView from './view/ExampleScreenView'; class ExampleScreen extends Screen { Index: main/example-sim/js/example/view/ControlPanel.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/example-sim/js/example/view/ControlPanel.js b/main/example-sim/js/example/view/ControlPanel.ts rename from main/example-sim/js/example/view/ControlPanel.js rename to main/example-sim/js/example/view/ControlPanel.ts --- a/main/example-sim/js/example/view/ControlPanel.js (revision 86ca81c92a74f01ff427752a4299086d34828050) +++ b/main/example-sim/js/example/view/ControlPanel.ts (date 1622776673430) @@ -16,14 +16,20 @@ import Panel from '../../../../sun/js/Panel.js'; import exampleSim from '../../exampleSim.js'; import exampleSimStrings from '../../exampleSimStrings.js'; +import ExampleModel from '../model/ExampleModel'; + +declare interface ControlPanelOptions { + xMargin?: number, + yMargin?: number +} class ControlPanel extends Panel { /** * @param {ExampleModel} model - the model for the entire screen - * @param {Object} [options] - scenery options for rendering the control panel, see the constructor for options + * @param {ControlPanelOptions} [options] - scenery options for rendering the control panel, see the constructor for options */ - constructor( model, options ) { + constructor( model: ExampleModel, options?: ControlPanelOptions ) { // Demonstrate a common pattern for specifying options and providing default values options = merge( { @@ -34,9 +40,10 @@ }, options ); // 'Flip Polarity' button + // @ts-ignore const flipButton = new TextPushButton( exampleSimStrings.flipPolarity, { font: new PhetFont( 16 ), - baseColor: 'yellow', + baseColor: 'green', xMargin: 10, listener: () => { const orientation = model.barMagnet.orientationProperty.get() + Math.PI; Index: main/example-sim/js/example/view/ExampleScreenView.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/example-sim/js/example/view/ExampleScreenView.js b/main/example-sim/js/example/view/ExampleScreenView.ts rename from main/example-sim/js/example/view/ExampleScreenView.js rename to main/example-sim/js/example/view/ExampleScreenView.ts --- a/main/example-sim/js/example/view/ExampleScreenView.js (revision 86ca81c92a74f01ff427752a4299086d34828050) +++ b/main/example-sim/js/example/view/ExampleScreenView.ts (date 1622774213351) @@ -14,14 +14,15 @@ import ModelViewTransform2 from '../../../../phetcommon/js/view/ModelViewTransform2.js'; import exampleSim from '../../exampleSim.js'; import BarMagnetNode from './BarMagnetNode.js'; -import ControlPanel from './ControlPanel.js'; +import ControlPanel from './ControlPanel'; +import ExampleModel from '../model/ExampleModel'; class ExampleScreenView extends ScreenView { /** * @param {ExampleModel} model - the model for the entire screen */ - constructor( model ) { + constructor( model: ExampleModel ) { super( { layoutBounds: new Bounds2( 0, 0, 768, 504 ) @@ -33,8 +34,8 @@ this.addChild( new BarMagnetNode( model.barMagnet, modelViewTransform ) ); this.addChild( new ControlPanel( model, { - x: 50, - y: 50 + xMargin: 10, + yMargin: 10 } ) ); } } Index: main/chipper/package.json IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/package.json b/main/chipper/package.json --- a/main/chipper/package.json (revision cb0d287b7dec072d5014a82a886b24b9a8369310) +++ b/main/chipper/package.json (date 1622774113702) @@ -29,8 +29,10 @@ "request-promise-native": "^1.0.7", "taffydb": "^2.7.3", "terser": "~4.6.4", - "webpack": "^4.41.4", - "webpack-cli": "^3.3.10", + "typescript": "~4.3.2", + "ts-loader": "~9.2.2", + "webpack": "^5.38.1", + "webpack-cli": "^4.7.0", "webpack-dev-server": "^3.10.1", "html-webpack-plugin": "4.0.0-beta.11", "memfs": "^3.0.3" ```

More ideas about transpiling:

```diff Index: main/chipper/js/grunt/Gruntfile.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/js/grunt/Gruntfile.js b/main/chipper/js/grunt/Gruntfile.js --- a/main/chipper/js/grunt/Gruntfile.js (revision 36af2fce595680494cb9668d06dfd8baa2651731) +++ b/main/chipper/js/grunt/Gruntfile.js (date 1622865301694) @@ -13,6 +13,9 @@ const buildRunnable = require( './buildRunnable' ); const buildStandalone = require( './buildStandalone' ); const commitsSince = require( './commitsSince' ); +const compileTypescript = require( './compileTypescript' ); +const transpileTypescript = require( './transpileTypescript' ); +const tsPatchFilenames = require( './tsPatchFilenames' ); const phetioCompareAPISets = require( '../phet-io/phetioCompareAPISets' ); const generateA11yViewHTML = require( './generateA11yViewHTML' ); const generateDevelopmentColorsHTML = require( './generateDevelopmentColorsHTML' ); @@ -120,6 +123,15 @@ } grunt.file.mkdir( buildDirectory ); } ) ); + + grunt.registerTask( 'tsc', + 'Runs the typescript compiler', + wrapTask( async () => { + // const buildDirectory = `../${repo}/tsconfig.json`; + // compileTypescript( buildDirectory ); + + await tsPatchFilenames( repo, 'phet' ); + } ) ); grunt.registerTask( 'build-images', 'Build images only', Index: main/chipper/tsconfig-proj.json IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/tsconfig-proj.json b/main/chipper/tsconfig-proj.json new file mode 100644 --- /dev/null (date 1622817743997) +++ b/main/chipper/tsconfig-proj.json (date 1622817743997) @@ -0,0 +1,105 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ + "module": "ES2020", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + "allowJs": true, /* Allow javascript files to be compiled. */ + "checkJs": false, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./dist", /* Redirect output structure to the directory. */ + "rootDir": "../", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": false, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + }, + "include": [ + "./phet-types.d.tstambo/js/demo/testing/view/TestingScreenView.js", + "../chipper/phet-types.d.ts", + "../chipper/js/getStringModule.js", + "../chipper/js/sim-tests/qunitStart.js", + "../axon/js/**/*", + "../brand/js/**/*", + "../brand/phet/js/Brand.js", + "../brand/phet/images/**/*", + "../dot/js/**/*", + "../joist/js/**/*", + "../joist/images/**/*", + "../joist/sounds/**/*", + "../kite/js/**/*", + "../phet-core/js/**/*", + "../phet-io/js/**/*", + "../phetcommon/js/**/*", + "../scenery/js/**/*", + "../sherpa/js/**/*", + "../sherpa/lib/lodash-4.17.4.min.js", + "../sun/js/**/*", + "../scenery-phet/js/**/*", + "../scenery-phet/images/**/*", + "../scenery-phet/mipmaps/**/*", + "../scenery-phet/sounds/**/*", + "../sherpa/lib/game-up-camera-1.0.0.js", + "../tambo/js/**/*", + "../tambo/sounds/**/*", + "../tambo/images/**/*", + "../tandem/js/**/*", + "../twixt/js/**/*", + "../utterance-queue/js/**/*" + ] +} \ No newline at end of file Index: main/example-sim/tsconfig.json IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/example-sim/tsconfig.json b/main/example-sim/tsconfig.json new file mode 100644 --- /dev/null (date 1622818672708) +++ b/main/example-sim/tsconfig.json (date 1622818672708) @@ -0,0 +1,82 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ + "module": "ES2020", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + "allowJs": true, /* Allow javascript files to be compiled. */ + "checkJs": false, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": false, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "../chipper/dist", /* Redirect output structure to the directory. */ + "rootDir": "../", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ +// "composite": false, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": false, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ +// "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + }, + "references": [ + { + "path": "../chipper" + } + ], + "files": ["js/example-sim-main.js"], +// "include": [ +// "js/", +// "images/" +// ] +} \ No newline at end of file Index: main/example-sim/example-sim_en.html IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/example-sim/example-sim_en.html b/main/example-sim/example-sim_en.html --- a/main/example-sim/example-sim_en.html (revision 86ca81c92a74f01ff427752a4299086d34828050) +++ b/main/example-sim/example-sim_en.html (date 1622865241392) @@ -124,7 +124,7 @@ // Module loading in compilation-free (development) mode will be kicked off once strings are loaded. // This is done in load-unbuilt-strings.js - window.phet.chipper.loadModules = () => loadURL( 'js/example-sim-main.js', 'module' ); + window.phet.chipper.loadModules = () => loadURL( '../chipper/dist/example-sim/js/example-sim-main.js', 'module' ); \ No newline at end of file Index: main/chipper/js/grunt/webpackBuild.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/js/grunt/webpackBuild.js b/main/chipper/js/grunt/webpackBuild.js --- a/main/chipper/js/grunt/webpackBuild.js (revision 36af2fce595680494cb9668d06dfd8baa2651731) +++ b/main/chipper/js/grunt/webpackBuild.js (date 1622827365632) @@ -81,6 +81,8 @@ const compiler = webpack( { + stats: 'verbose', + // We uglify as a step after this, with many custom rules. So we do NOT optimize or uglify in this step. optimization: { minimize: false @@ -96,6 +98,27 @@ path: path.resolve( __dirname, `../../${ChipperConstants.BUILD_DIR}` ), filename: `${repo}.js` }, + module: { + rules: [ + { + test: /\.ts$/, use: [ + { + loader: 'ts-loader', + options: { + // transpileOnly: true // cuts time from 6047ms to 4932ms and avoid errors + // configFile: path.resolve( __dirname, `../${repo}/tsconfig.js` ) + projectReferences: true, + configFile: '/Users/samreid/apache-document-root/main/example-sim/tsconfig.json' + } + } + ] + } + ] + }, + + resolve: { + extensions: [ '.ts', '.js' ] + }, // {Array.} plugins: @@ -107,6 +130,12 @@ } ); compiler.run( ( err, stats ) => { + + console.log( stats.toString( { + stats:'verbose', + // Add console colors + colors: true, + } ) ); if ( err || stats.hasErrors() ) { console.error( 'Webpack build errors:', stats.compilation.errors ); reject( err || stats.compilation.errors[ 0 ] ); Index: main/example-sim/package.json IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/example-sim/package.json b/main/example-sim/package.json --- a/main/example-sim/package.json (revision 86ca81c92a74f01ff427752a4299086d34828050) +++ b/main/example-sim/package.json (date 1622817744086) @@ -7,7 +7,9 @@ "url": "https://github.com/phetsims/example-sim.git" }, "devDependencies": { - "grunt": "~1.4.0" + "grunt": "~1.4.0", + "ts-loader": "^9.2.2", + "webpack": "^5.38.1" }, "phet": { "requirejsNamespace": "EXAMPLE_SIM", @@ -23,4 +25,4 @@ "eslintConfig": { "extends": "../chipper/eslint/sim_eslintrc.js" } -} \ No newline at end of file +} Index: main/chipper/js/grunt/getStringMap.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/js/grunt/getStringMap.js b/main/chipper/js/grunt/getStringMap.js --- a/main/chipper/js/grunt/getStringMap.js (revision 36af2fce595680494cb9668d06dfd8baa2651731) +++ b/main/chipper/js/grunt/getStringMap.js (date 1622826535264) @@ -100,7 +100,19 @@ }; // Load the file contents of every single JS module that used any strings - const usedFileContents = usedModules.map( usedModule => fs.readFileSync( `../${usedModule}`, 'utf-8' ) ); + const usedFileContents = usedModules.map( usedModule => { + if ( usedModule.startsWith( '..' ) || usedModule.includes( 'node_modules' ) ) { + return ''; + } + try { + return fs.readFileSync( `../${usedModule}`, 'utf-8' ); + } + catch( e ) { + + // maybe a directory + return ''; + } + } ); // Compute which repositories contain one more more used strings (since we'll need to load string files for those // repositories). Index: main/chipper/js/grunt/compileTypescript.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/js/grunt/compileTypescript.js b/main/chipper/js/grunt/compileTypescript.js new file mode 100644 --- /dev/null (date 1622817707525) +++ b/main/chipper/js/grunt/compileTypescript.js (date 1622817707525) @@ -0,0 +1,54 @@ +// Copyright 2021, University of Colorado Boulder +'use strict'; + +/** + * Adapted from https://github.com/Microsoft/TypeScript/issues/6387 + * and + * https://stackoverflow.com/questions/57333825/can-you-pull-in-excludes-includes-options-in-typescript-compiler-api + * + * @author Sam Reid (PhET Interactive Simulations) + */ + +const typescript = require( 'typescript' ); +const fs = require( 'fs' ); +const path = require( 'path' ); + +function reportErrors( diagnostics ) { + diagnostics.forEach( diagnostic => { + let message = 'Error'; + if ( diagnostic.file ) { + const where = diagnostic.file.getLineAndCharacterOfPosition( diagnostic.start ); + message += ` ${diagnostic.file.fileName} ${where.line}, ${where.character}`; + } + message += `: ${typescript.flattenDiagnosticMessageText( diagnostic.messageText, '\n' )}`; + console.log( message ); + } ); +} + +module.exports = function( configFileName ) { + + // Get filenames to compile from the config + // https://stackoverflow.com/questions/57333825/can-you-pull-in-excludes-includes-options-in-typescript-compiler-api + const configFile = typescript.readJsonConfigFile( configFileName, typescript.sys.readFile ); + const { fileNames } = typescript.parseJsonSourceFileConfigFileContent( + configFile, + typescript.sys, + './' + ); + + // Extract configuration from config file + // TODO: use the same config as above? See https://github.com/phetsims/tasks/issues/987 + const configFileString = fs.readFileSync( configFileName ).toString(); + const configFileJSON = typescript.parseConfigFileTextToJson( configFileName, configFileString ); + const config = typescript.parseJsonConfigFileContent( configFileJSON.config, typescript.sys, path.dirname( configFileName ) ); + if ( config.errors.length > 0 ) { + reportErrors( config.errors ); + } + + // Compile + const program = typescript.createProgram( fileNames, config.options ); + const emitResult = program.emit(); + + // Report errors + reportErrors( typescript.getPreEmitDiagnostics( program ).concat( emitResult.diagnostics ) ); +}; \ No newline at end of file Index: main/chipper/phet-types.d.ts IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/phet-types.d.ts b/main/chipper/phet-types.d.ts new file mode 100644 --- /dev/null (date 1622817743994) +++ b/main/chipper/phet-types.d.ts (date 1622817743994) @@ -0,0 +1,27 @@ +/* eslint-disable */ + +// import Tandem from "../../../tandem/js/Tandem"; + +declare var assert: any; +declare var phet: any; +declare var phetio: any; +declare var _: any; +declare var assertSlow: any; +declare var sceneryLog: any; +declare var scenery: any; +// declare var Enumeration: any; +declare var QUnit: any; + +declare var module: any + +declare interface PhetioObject { + greeting: string; + duration?: number; + color?: string; + // tandem: Tandem; +} + +declare interface Enumeration { + NOTIFY: string; + IDENTITY: string; +} \ No newline at end of file Index: main/chipper/js/grunt/tsPatchFilenames.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/js/grunt/tsPatchFilenames.js b/main/chipper/js/grunt/tsPatchFilenames.js new file mode 100644 --- /dev/null (date 1622866913271) +++ b/main/chipper/js/grunt/tsPatchFilenames.js (date 1622866913271) @@ -0,0 +1,53 @@ +// Copyright 2017-2020, University of Colorado Boulder +const ts = require( 'typescript' ); // eslint-disable-line require-statement-match +const fs = require( 'fs' ); +const path = require( 'path' ); + +const visit = ( source, destination ) => { + if ( fs.lstatSync( source ).isDirectory() ) { + fs.readdirSync( source ).forEach( file => { + if ( file !== 'node_modules' && + file !== '.git' && + file !== 'build' && + file !== 'transpiled' && + file !== 'dist' ) { + visit( source + '/' + file, destination + '/' + file ); + } + } ); + } + else if ( fs.lstatSync( source ).isFile() ) { + // if ( source.endsWith( '.js' ) ) { + // + // fs.mkdirSync( path.dirname( destination ), { recursive: true } ); + // + // const contents = fs.readFileSync( source ).toString(); + // const replaced = contents.split( '.ts\';' ).join( '.js\';' ); // HACK alert + // + // if ( !fs.existsSync( destination ) || fs.readFileSync( destination ).toString() !== replaced ) { + // console.log( 'copying ' + source + ' to ' + destination ); + // fs.writeFileSync( destination, replaced ); + // } + // } + if ( source.endsWith( '.ts' ) || source.endsWith( '.js' ) ) { + + const contents = fs.readFileSync( source ).toString(); + const replaced = contents.split( '.ts\';' ).join( '.js\';' ); // HACK alert + const module = ts.transpileModule( replaced, { compilerOptions: { module: ts.ModuleKind.ES2015 } } ); + // const module = ts.transpileModule( replaced, { compilerOptions: { module: ts.ModuleKind.AMD } } ); + // const module = ts.transpileModule( replaced, { compilerOptions: { module: ts.ModuleKind.CommonJS } } ); + + const outfile = destination.split( '.ts' ).join( '.js' ); + fs.mkdirSync( path.dirname( outfile ), { recursive: true } ); + if ( !fs.existsSync( outfile ) || fs.readFileSync( outfile ).toString() !== module.outputText ) { + console.log( 'writing transpiled ts file: ' + outfile ); + fs.writeFileSync( outfile, module.outputText ); + } + } + } +}; + +module.exports = () => { + const destination = '/Users/samreid/apache-document-root/main/chipper/dist/'; + + visit( destination ); +}; \ No newline at end of file Index: main/chipper/js/grunt/transpileTypescript.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/js/grunt/transpileTypescript.js b/main/chipper/js/grunt/transpileTypescript.js new file mode 100644 --- /dev/null (date 1622864410921) +++ b/main/chipper/js/grunt/transpileTypescript.js (date 1622864410921) @@ -0,0 +1,76 @@ +// Copyright 2017-2020, University of Colorado Boulder +const ts = require( 'typescript' ); // eslint-disable-line require-statement-match +const fs = require( 'fs' ); +const path = require( 'path' ); + +const visit = ( source, destination ) => { + if ( fs.lstatSync( source ).isDirectory() ) { + fs.readdirSync( source ).forEach( file => { + if ( file !== 'node_modules' && + file !== '.git' && + file !== 'build' && + file !== 'transpiled' && + file !== 'dist' ) { + visit( source + '/' + file, destination + '/' + file ); + } + } ); + } + else if ( fs.lstatSync( source ).isFile() ) { + // if ( source.endsWith( '.js' ) ) { + // + // fs.mkdirSync( path.dirname( destination ), { recursive: true } ); + // + // const contents = fs.readFileSync( source ).toString(); + // const replaced = contents.split( '.ts\';' ).join( '.js\';' ); // HACK alert + // + // if ( !fs.existsSync( destination ) || fs.readFileSync( destination ).toString() !== replaced ) { + // console.log( 'copying ' + source + ' to ' + destination ); + // fs.writeFileSync( destination, replaced ); + // } + // } + if ( source.endsWith( '.ts' ) || source.endsWith('.js') ) { + + const contents = fs.readFileSync( source ).toString(); + const replaced = contents.split( '.ts\';' ).join( '.js\';' ); // HACK alert + const module = ts.transpileModule( replaced, { compilerOptions: { module: ts.ModuleKind.ES2015 } } ); + // const module = ts.transpileModule( replaced, { compilerOptions: { module: ts.ModuleKind.AMD } } ); + // const module = ts.transpileModule( replaced, { compilerOptions: { module: ts.ModuleKind.CommonJS } } ); + + const outfile = destination.split( '.ts' ).join( '.js' ); + fs.mkdirSync( path.dirname( outfile ), { recursive: true } ); + if ( !fs.existsSync( outfile ) || fs.readFileSync( outfile ).toString() !== module.outputText ) { + console.log( 'writing transpiled ts file: ' + outfile ); + fs.writeFileSync( outfile, module.outputText ); + } + } + } +}; + +module.exports = () => { + const root = '/Users/samreid/apache-document-root/main/'; + const destination = '/Users/samreid/apache-document-root/main/chipper/transpiled/'; + + const repos = [ + 'assert', + 'axon', + 'babel', + 'brand', + 'chipper', + 'dot', + 'example-sim', + 'joist', + 'kite', + 'phet-core', + 'phetcommon', + 'phetmarks', + 'query-string-machine', + 'scenery', + 'scenery-phet', + 'sherpa', + 'sun', + 'tambo', + 'tandem', + 'utterance-queue' + ]; + repos.forEach( repo => visit( root + repo, destination + repo ) ); +}; \ No newline at end of file Index: main/chipper/.gitignore IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/.gitignore b/main/chipper/.gitignore --- a/main/chipper/.gitignore (revision 36af2fce595680494cb9668d06dfd8baa2651731) +++ b/main/chipper/.gitignore (date 1622854159657) @@ -9,3 +9,5 @@ *.sublime-workspace package-lock.json build +dist +transpiled \ No newline at end of file Index: main/scenery-phet/js/buttons/ResetAllButton.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/scenery-phet/js/buttons/ResetAllButton.js b/main/scenery-phet/js/buttons/ResetAllButton.ts rename from main/scenery-phet/js/buttons/ResetAllButton.js rename to main/scenery-phet/js/buttons/ResetAllButton.ts --- a/main/scenery-phet/js/buttons/ResetAllButton.js (revision 437c015bd31ad9a8e32786ec474331613fbf6003) +++ b/main/scenery-phet/js/buttons/ResetAllButton.ts (date 1622838596303) @@ -52,6 +52,9 @@ innerContent: resetAllButtonNameString }, options ); + // const x: number = 7; + // console.log( x ); + const passedInListener = options.listener; // Wrap the listener for all cases, since PhET-iO won't be able to call this.isPhetioInstrumented() until the super call is complete. Index: main/chipper/js/grunt/buildRunnable.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/js/grunt/buildRunnable.js b/main/chipper/js/grunt/buildRunnable.js --- a/main/chipper/js/grunt/buildRunnable.js (revision 36af2fce595680494cb9668d06dfd8baa2651731) +++ b/main/chipper/js/grunt/buildRunnable.js (date 1622826315076) @@ -126,8 +126,13 @@ const dependencies = await getDependencies( repo ); webpackResult.usedModules.forEach( moduleDependency => { - const moduleRepo = moduleDependency.slice( 0, moduleDependency.indexOf( path.sep ) ); - assert( Object.keys( dependencies ).includes( moduleRepo ), `repo ${moduleRepo} missing from package.json's phetLibs for ${moduleDependency}` ); + const index = moduleDependency.indexOf( path.sep ); + if ( index >= 0 ) { + const moduleRepo = moduleDependency.slice( 0, index ); + if ( !moduleRepo.startsWith( '..' ) ) { + assert( Object.keys( dependencies ).includes( moduleRepo ), `repo ${moduleRepo} missing from package.json's phetLibs for ${moduleDependency}` ); + } + } } ); const version = packageObject.version; // Include the one-off name in the version Index: main/chipper/eslint/.eslintignore IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/eslint/.eslintignore b/main/chipper/eslint/.eslintignore --- a/main/chipper/eslint/.eslintignore (revision 36af2fce595680494cb9668d06dfd8baa2651731) +++ b/main/chipper/eslint/.eslintignore (date 1622817707519) @@ -12,4 +12,5 @@ ../phet-io-website/root/assets/js/QueryStringMachine_1.0.js ../phet-io-website/root/assets/js/QueryStringMachine_2.0.js ../phet-io-website/root/assets/js/phet-io-ga.js -../installer-builder/temp/** \ No newline at end of file +../installer-builder/temp/** +../chipper/dist \ No newline at end of file Index: main/example-sim/js/example/view/ControlPanel.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/example-sim/js/example/view/ControlPanel.js b/main/example-sim/js/example/view/ControlPanel.ts rename from main/example-sim/js/example/view/ControlPanel.js rename to main/example-sim/js/example/view/ControlPanel.ts --- a/main/example-sim/js/example/view/ControlPanel.js (revision 86ca81c92a74f01ff427752a4299086d34828050) +++ b/main/example-sim/js/example/view/ControlPanel.ts (date 1622866237882) @@ -9,21 +9,30 @@ */ import merge from '../../../../phet-core/js/merge.js'; -import ResetAllButton from '../../../../scenery-phet/js/buttons/ResetAllButton.js'; +import ResetAllButton from '../../../../scenery-phet/js/buttons/ResetAllButton'; import PhetFont from '../../../../scenery-phet/js/PhetFont.js'; import VBox from '../../../../scenery/js/nodes/VBox.js'; import TextPushButton from '../../../../sun/js/buttons/TextPushButton.js'; import Panel from '../../../../sun/js/Panel.js'; import exampleSim from '../../exampleSim.js'; import exampleSimStrings from '../../exampleSimStrings.js'; +import ExampleModel from '../model/ExampleModel.js'; + +declare interface ControlPanelOptions { + xMargin?: number, + yMargin?: number +} + +const x: number = 'hello'; +console.log( x ); class ControlPanel extends Panel { /** * @param {ExampleModel} model - the model for the entire screen - * @param {Object} [options] - scenery options for rendering the control panel, see the constructor for options + * @param {ControlPanelOptions} [options] - scenery options for rendering the control panel, see the constructor for options */ - constructor( model, options ) { + constructor( model: ExampleModel, options?: ControlPanelOptions ) { // Demonstrate a common pattern for specifying options and providing default values options = merge( { @@ -34,9 +43,10 @@ }, options ); // 'Flip Polarity' button + // @ts-ignore const flipButton = new TextPushButton( exampleSimStrings.flipPolarity, { font: new PhetFont( 16 ), - baseColor: 'yellow', + baseColor: 'green', xMargin: 10, listener: () => { const orientation = model.barMagnet.orientationProperty.get() + Math.PI; Index: main/example-sim/js/example/ExampleScreen.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/example-sim/js/example/ExampleScreen.js b/main/example-sim/js/example/ExampleScreen.js --- a/main/example-sim/js/example/ExampleScreen.js (revision 86ca81c92a74f01ff427752a4299086d34828050) +++ b/main/example-sim/js/example/ExampleScreen.js (date 1622866105983) @@ -10,7 +10,7 @@ import Screen from '../../../joist/js/Screen.js'; import exampleSim from '../exampleSim.js'; import ExampleModel from './model/ExampleModel.js'; -import ExampleScreenView from './view/ExampleScreenView.js'; +import ExampleScreenView from './view/ExampleScreenView'; class ExampleScreen extends Screen { Index: main/chipper/tsconfig.json IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/tsconfig.json b/main/chipper/tsconfig.json new file mode 100644 --- /dev/null (date 1622866838407) +++ b/main/chipper/tsconfig.json (date 1622866838407) @@ -0,0 +1,79 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */ + "module": "ES2020", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + "allowJs": true, /* Allow javascript files to be compiled. */ + "checkJs": false, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */ + // "declaration": true, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ + "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + "outDir": "./dist", /* Redirect output structure to the directory. */ + "rootDir": "../", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ +// "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": false, /* Enable all strict type-checking options. */ + // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + // "strictNullChecks": true, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ + // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ + // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + }, + "include": [ + "../example-sim/js/example-sim-main.js", + "../gravity-and-orbits/js/gravity-and-orbits-main.js", + "../brand/js/**/*", + "../brand/phet/js/Brand.js", + "../brand/phet/images/**/*", + ] +} \ No newline at end of file Index: main/example-sim/js/example/view/ExampleScreenView.js IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/example-sim/js/example/view/ExampleScreenView.js b/main/example-sim/js/example/view/ExampleScreenView.ts rename from main/example-sim/js/example/view/ExampleScreenView.js rename to main/example-sim/js/example/view/ExampleScreenView.ts --- a/main/example-sim/js/example/view/ExampleScreenView.js (revision 86ca81c92a74f01ff427752a4299086d34828050) +++ b/main/example-sim/js/example/view/ExampleScreenView.ts (date 1622866237880) @@ -14,14 +14,15 @@ import ModelViewTransform2 from '../../../../phetcommon/js/view/ModelViewTransform2.js'; import exampleSim from '../../exampleSim.js'; import BarMagnetNode from './BarMagnetNode.js'; -import ControlPanel from './ControlPanel.js'; +import ControlPanel from './ControlPanel'; +import ExampleModel from '../model/ExampleModel.js'; class ExampleScreenView extends ScreenView { /** * @param {ExampleModel} model - the model for the entire screen */ - constructor( model ) { + constructor( model: ExampleModel ) { super( { layoutBounds: new Bounds2( 0, 0, 768, 504 ) @@ -33,8 +34,8 @@ this.addChild( new BarMagnetNode( model.barMagnet, modelViewTransform ) ); this.addChild( new ControlPanel( model, { - x: 50, - y: 50 + xMargin: 10, + yMargin: 10 } ) ); } } Index: main/chipper/package.json IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== diff --git a/main/chipper/package.json b/main/chipper/package.json --- a/main/chipper/package.json (revision 36af2fce595680494cb9668d06dfd8baa2651731) +++ b/main/chipper/package.json (date 1622828694783) @@ -29,6 +29,8 @@ "request-promise-native": "^1.0.7", "taffydb": "^2.7.3", "terser": "~4.6.4", + "typescript": "~4.3.2", + "ts-loader": "~9.2.2", "webpack": "^5.38.1", "webpack-cli": "^4.7.0", "webpack-dev-server": "^3.11.2", ```
samreid commented 3 years ago

Would it deal with scenery boilerplate like that in setLeftTop() etc.?

We currently have

  /**
   * Sets the position of the upper-left corner of this node's bounds to the specified point.
   * @public
   *
   * @param {Vector2} leftTop
   * @returns {Node} - For chaining
   */
  setLeftTop( leftTop ) {
    assert && assert( leftTop instanceof Vector2 && leftTop.isFinite(), 'leftTop should be a finite Vector2' );

    const currentLeftTop = this.getLeftTop();
    if ( currentLeftTop.isFinite() ) {
      this.translate( leftTop.minus( currentLeftTop ), true );
    }

    return this;
  }

  /**
   * See setLeftTop() for more information
   * @public
   *
   * @param {Vector2} value
   */
  set leftTop( value ) {
    this.setLeftTop( value );
  }

  /**
   * Returns the upper-left corner of this node's bounds.
   * @public
   *
   * @returns {Vector2}
   */
  getLeftTop() {
    return this.getBounds().getLeftTop();
  }

  /**
   * See getLeftTop() for more information
   * @public
   *
   * @returns {Vector2}
   */
  get leftTop() {
    return this.getLeftTop();
  }

Some teams have a policy of not repeating type information in documentation. For instance, Item 30 in "Effective Typescript" by Vanderkam is "Don't Repeat Type Information in Documentation". If we choose a philosophy like that, the above code could be compressed like so. We no longer would need @public because the methods aren't marked private. Return and parameter types are documented in the code instead of the JSDoc. We don't need to check leftTop instanceof Vector2

  /** Sets the position of the upper-left corner of this node's bounds to the specified point. */
  setLeftTop( leftTop: Vector2 ): Node {
    assert && assert( leftTop.isFinite(), 'leftTop should be a finite Vector2' );

    const currentLeftTop = this.getLeftTop();
    if ( currentLeftTop.isFinite() ) {
      this.translate( leftTop.minus( currentLeftTop ), true );
    }

    return this;
  }

  set leftTop( value : Vector2 ) { this.setLeftTop( value ); }

  /** Returns the upper-left corner of this node's bounds.*/
  getLeftTop(): Vector2 { return this.getBounds().getLeftTop(); }

  /** See getLeftTop() for more information */
  get leftTop() { return this.getLeftTop();  }

I also compressed trivial comments and implementations to single lines, but that is optional.

samreid commented 3 years ago

Typescript update at dev meeting June 10:

What's working:

What's next:

CK: Looks great! JO: I would hope for the common code first becoming typescript. Would we have to do sims first? SR: We can rename files, and use // @ts-nocheck to make it easy for any file to use typescript. But for a learning and ramping up phase, it may be best to start with leaves (like sims) as we learn typescript. UPDATE: I elaborated on this in https://github.com/phetsims/tasks/issues/1088 CK: Now that I've been thinking about typescript, I'm noticing more and more things that would save me time.

samreid commented 3 years ago

This investigation has gone well and we stand to benefit significantly from TypeScript. Let's close this issue and open a "Migrate to Typescript" epic issue in chipper.