Supposedly 300,000 lines of TypeScript project practice dry cargo?

Over the years of TypeScript project development, we have changed the project structure several times to accommodate additional project splitting requirements, resulting in the existing common project structure. This article will introduce this common project structure and some of the practical experiences associated with it.

Package structure

Projects, whether simple or complex, usually consist of one or more packages. First let’s discuss the project structure of a single package, as an example:

# the source file
src/
  library/
    .eslintrc.js
    tsconfig.json
    index.ts
  program/
    .eslintrc.js
    tsconfig.json
    main.ts
test/
  .eslintrc.js
  tsconfig.json
  some.test.ts
prettier.config.js
tsconfig.json
package.json

# Build artifacts
bld/
  library/
    index.js
  program/
    main.js
.bld-cache/
  test/
    some.test.jsCopy the code

First of all:

  • The SRC directory places the source code for the project that needs to be built for the package to be split. In the example, library, program, and Test are all horizontal in a sense. The difference between test and the other two is that it is not supposed to produce build artifacts (not exactly, see below).
  • Library and program are two common names that correspond to what is used as a library and what is executed as a program. Many times there is only one of these folders under SRC, but many times there are more.
  • For source code used as a library, our entry file uses index.ts; As part of the program execution, the entry file uses main.ts.
  • Each split project in the package has its own eslintrc and tsConfig to ensure flexibility in configuration and to differentiate the rules and types of different runtimes. In the project we use the run-in-every gadget to implement ESLint.
  • A uniform prettier configuration is used within a package because Prettier focuses on non-functional code styles, so a single configuration can be used directly for the entire package.
  • Global tsconfig for reference to split projects for easy usetsc --buildConduct a unified build. And of course in the split project, we did it allcompositeCompile options and reference dependencies.
  • Eslint, tsconfig, Prettier configuration We all have additional warehouse maintenance configurations and some of our own rules, more on project organization later.

Construction products:

  • The first is the BLD (build) folder that corresponds to SRC, which is a matter of personal habit. Other common names include out, lib, dist, and so on. Lib, of course, is not a good fit in this structure; you can choose it according to your preference.
  • Then there is the.bld-cache folder for split projects that “don’t need to build artifacts,” which is not required at the application level, but in order to take advantage of TypeScripttsc --buildCache, we specifically added such a folder.

Multi-package project structure

In many cases, one package is no longer enough for us, but multi-package projects are similar to single-package projects in our approach:

packages/
  gateway/
    src/program/
      .eslintrc.js
      tsconfig.json
      main.ts
    package.json
  server/
    src/program/
    scripts/
    package.json
  web/
    src/
      program/
      service-worker/
    package.json
  shared/
    src/library/
    package.json
prettier.config.js
tsconfig.json
package.jsonCopy the code

Multipackage projects provide an additional level (or levels) of directories for project organization and finer package dependency management than single-package projects, but still share the same prettier configuration.

We used Yarn Workspace for dependency management and LerNA for package distribution. Yarn Workspace automatically creates symbolic links for packages in node_modules to facilitate the import of other packages.

Yarn-deduplicate is an essential tool for using YARN. It is basically a standard method for solving type conflicts and other problems. CI is strongly recommended.

Module organization

For TypeScript source files, we treat prefixed files as files to be exported to (folder delimited) or as entry files for extraterritorial use, and files that begin with @ as in-domain files that are not exported. Here’s an example:

src/library/
  utils/
    index.ts
    string.ts
    object.ts
  internal.ts
  feature.ts
  index.tsCopy the code

The above files are in our agreement and everything will be exported.

Specifically, the library/utils/index.ts file contains the following contents:

export * from './string';
export * from './object';
Copy the code

Library /index.ts contains the following contents:

export * from './utils';
export * from './feature';
export * from './internal';
Copy the code

If you prefix the utils folder and internal. Ts files with @ :

src/library/
  @utils/
    index.ts
    string.ts
    object.ts
  @internal.ts
  feature.ts
  index.tsCopy the code

The corresponding library/index.ts content should be:

export * from './feature';
Copy the code

This works well in practice, but sometimes there are so many things to export in a large module or an entire package that prefixes are necessary to avoid name conflicts. To remedy this problem, we started experimenting with the Namespace scheme as a supplement.

Ts file is added to namespace.ts, and the contents of the index.ts file are changed to:

export * as AwesomeNamespace from './namespace';
Copy the code

For convenience, we wrote an ESLint rule called scoped-Modules to add validation and auto-repair to the above rules.

After the

Many details are not developed due to space and to avoid out-of-focus, please leave your questions in the comments section

Photo by Joel Filipe on Unsplash

Makeflow (makeFlow.com) enables team experiences to be documented in detail in the process, guiding and validating work practices. Each iteration of experience can be naturally “pushed” to the entire team through the execution of tasks, removing multiple barriers in the workflow from idea to practice and from practice to improvement. Big to product iteration management, small to monitor alarm disposal: record, practice, record again, write every progress into the team gene — continuation, change, replicable.