Skip to content

Latest commit

 

History

History
298 lines (228 loc) · 14.2 KB

CONTRIBUTING.md

File metadata and controls

298 lines (228 loc) · 14.2 KB

TI-JS Development Guide

If you're interested in getting involved (or even just learning more about TI-JS), you've come to the right place.

Other resources:

Table of contents

Community

Issues

If you run into an issue, file it here. Make sure to include tails about what you're trying to do, and why.

Pull Requests

Before making pull requests, reach out to the maintainer via email.

Once you've published a pull request, automatic checks will make sure tests pass and linting looks good. If there are any issues, fix them and update your PR.

If you're making updates to the grammar or adding new functionality, make sure to update the ROADMAP.md.

Development

Commands

Make sure you have node installed then run:

npm install

You can now then run any of the scripts in package.json. Here's a list of the more useful commands:

Command Description
npm run bt Build and test. Also updates API documentation. Will fail if there are lint errors.
npm run lint:fix Fixes simple lint errors.
npm run next Creates a new version, pushes it to npm, and pushes to github. Will fail if there are lint warnings or unresolved API changes. If this happens, run npm run bt.
npm start Starts up a dev server at http://localhost:9080/playground/
npm run cov Print test coverage results.

You're unlikely to use the other commands directly, but here they are:

Command Description
npm run clean Cleans build directory.
npm run build Builds with webpack.
npm test Runs tests. Note that you'll want to build first, so just use npm run bt instead.
npm run lint Print lint warnings and errors. Fails if there lint errors.
npm run lint:prod Same as above, fail even on warnings.
npm run api:extractor Runs the API extractor.
npm run api:post Copy API output to build output.
npm run api:prod Fails if there have been changes to the TypeScript API without corresponding updates to the API file.
npm run api Updates the API file from the TypeScript API.
npm run bump Increments the patch version.
npm run bt:prod Build and test, but fail if there lint warnings or unconfirmed API changes.
npm run push Builds and publishes to NPM.
npm run ci:github Runs prod build for github CI.

Finally, there are encoding related commands. Again, these shouldn't be needed directly.

Command Description
encoding:install Install the node packages needed for encoding.
encoding:build Build the encoding table.
encoding:copy Copy the encoding table to the ./src/gen folder.
encoding:bc Build and copy.
encoding:hash:update Write the git hash of the encoding directory to ./src/gen.
encoding:hash:dirty Exit with an error code if the hash in ./src/gen has changed.
encoding:source:dirty Exit with an error code if /encoding has changed.
encoding:dirty Exit with an error code if either of the above have changed.
encoding:dirty:ci Exit with an error code if hash has changed (for CI).
encoding:warn Print a warning that the build will be slowed due to encoding build.
encoding:error Print an error message that encoding changes need to be handled, then exit with an error code.
encoding:run If there are encoding changes, run the encoding build.
encoding:run:prod If there are encoding changes, exit with an error code.
encoding:run:ci If there are encoding changes, exit with an error code (for CI).

Note that unless you're the maintainer, you won't have permissions to push directly to npm or github.

Code directory

File Category Description
.github/ 🔨 repo CI, github actions
api/ 🔎 api latest API goes here
docs/ 📚 docs hosts the website
sample/ 💡 sample sample projects that use this library
src/ 💻 code the source code
tests/ 🔬 tests the test framework
web/ 🔬 tests dev tools, plus the actual test cases
.eslintignore 🧹 lint files not to lint
.eslintrc.js 🧹 lint lint config and overrides
.gitignore 🔨 repo files not to track
.taprc 🔬 tests code coverage config
api-extractor.json 🔎 api API extraction config
CONTRIBUTING.md 📚 docs how to contribute
LICENSE 📚 docs license information
package-lock.json 📦 node npm lock file
package.json 📦 node node scripts and package dependencies
README.md 📚 docs the readme
tsconfig.json 📜 typescript typescript config and rules
tsdoc.json 🔎 api API extraction integration with typscript
webpack.config.js 🏗 webpack common build config
webpack.dev.js 🏗 webpack dev build config
webpack.prod.js 🏗 webpack prod build config

Tooling

While developing, you can use the following dev tools:

Tool Description
tests runs unit tests
playground debug programs and view the AST

The prod versions are linked above, but you can get dev builds with npm start.

Testing

Test cases are stored as a JS object in web/js/testCases.js.

You can run them in two ways:

  • npm run bt: this will build and run tests using Node-Tap. This is integrated using tests/e2e.js.
  • npm run start: this will build and run a devserver, which will let you view the test results at http://localhost:9080/tests/. You can view the production build of that site here.

TAP also gives coverage information, which you can view using npm run cov. The .taprc defines the minimum code coverage rules.

API

The library is typescript compatible, which means its types are exported in a ti.d.ts file. Normally this means handcrafting that file, but the API Extractor helps automate that.

For example, the Variable type in types.ts:

/**
 * @alpha
 */
export type Variable =
    NumberVariable
    | StringVariable
    | ListVariable

Compiles to this in the ti.api.md:

// @alpha (undocumented)
type Variable = NumberVariable | StringVariable | ListVariable;

Which then becomes this in ti.d.ts:

/**
 * @alpha
 */
declare type Variable = NumberVariable | StringVariable | ListVariable;

Note that we're in prerelease, so everything is @alpha and undocumented.

See the Commands table for the commands relevant to API extraction. In general, double check that the API isn't updated unintentionally.

Versioning

This project uses semantic versioning.

Since we're in prerelease, the major version is 0 and there's no changelog. Releases simply increment the patch version.

The project will move into release once there's at least one interesting use case. This will probably be demonstrating a real program (written for the calculator) running in the browser.

Auxillary projects

In addition to the library, there are various directories with their own build processes:

Directory Description
docs/ hosts the website
sample/node/ demos library usage with a node app
sample/node-ts/ demos library usage with a node app, using typescript
sample/web/ demos library usage with a web app
sample/web-ts/ demos library usage with a web app, using typescript
web/ website with dev tools

View their README files to learn more.

Architecture

Overview

Here's an overview of how the library works:

TI-JS Architecture diagram

Component Description
exec top-level helper
parser converts strings to objects that represent the AST
runtime sets up device memory and executes the AST
statement evaluates individual statements
iolib helps manage input and output
daemon executes statements in a tight loop
inject provides different implementations for web and node

Dependency injection

The library is available for both web apps (client side) and node apps (server side).

However, node and web don't have the same capabilities. For example, in a web app you can use window.postMessage to write a non-blocking loop, but there's no window in node.

To solve this, we use dependency injection:

TI-JS Dependency injection diagram

Component Description
🟪 looper this defines a Looper interface.
🟪 inject has getLooper and setLooper functions
🟦 looper.web implements Looper using window.
🟧 looper.node implements Looper using Worker.
🟦 inject.web calls setLooper with the web implementation
🟧 inject.node calls setLooper with the node implementation
🟦 web.ts calls init on inject.web, and exports common.ts
🟧 node.ts calls init on inject.node, and exports common.ts
common.ts defines the library, including...
daemon.ts calls getLooper on inject

If you're wondering we this isn't as simple as in if statement, the reason is that in some cases these are build time dependencies.

That means we need two separate builds, defined in webpack.config.js:

const web = merge(common, {
  target: 'web',
  entry: './src/web.ts',
  output: {
    path: path.resolve(__dirname, 'dist/web'),
    filename: 'ti.js',
  },
})

const node = merge(common, {
  target: 'node',
  entry: './src/node.ts',
  output: {
    path: path.resolve(__dirname, 'dist/node'),
    filename: 'ti.js',
  },
  externals: [nodeExternals()],
})

Incidentially, that's why node can do require('ti-js') while web has to do require('ti-js/dist/web/ti') - we can only define one default output path.

I/O

Input and output is mostly handled by iolib.ts.

Currently this supports using DOM elements for IO, but not anything like a real calculator. This will need to be revamped in order to support real programs.

Character encoding

TI-BASIC does not use ASCII, of course. Instead, it its own encoding commonly exported to computers in .8XP (or .83P) format.

For example, the For( token is 4 bytes in ASCII, but only a single byte in TI-BASIC encoding.

To keep things simple, this library only deals with ASCII. This means characters like θ are represented as &{theta}. However, some strings will automatically be converted (for example, -> will always be interpreted as &{->}.) There is not yet a way to opt out.

See the encoding subproject for creating a more strict character mapping.