diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 00000000..94f7db6a --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,120 @@ +name: CI +on: + pull_request: + branches: + - v2 + +# Ensure scripts are run with pipefail. See: +# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#exit-codes-and-error-action-preference +defaults: + run: + shell: bash + +jobs: + tests: + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - windows-latest + - macos-latest + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 + with: + node-version: "18.x" + - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + + - run: pnpm install + + # Grab localizations + - run: pnpm docs-sync pull microsoft/TypeScript-Website-localizations#main 1 + + # Build the packages + - run: pnpm bootstrap + - run: pnpm build + + # Verify it compiles + - run: pnpm build-site + + - uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + if: github.event_name == 'pull_request' && matrix.os == 'ubuntu-latest' + with: + name: site + path: packages/typescriptlang-org/public + + # Run all the package's tests + - run: pnpm test + + # danger for PR builds + - if: github.event_name == 'pull_request' && github.event.pull_request.base.repo.id == github.event.pull_request.head.repo.id && matrix.os == 'ubuntu-latest' + run: "pnpm danger ci" + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - run: | + git add . + if ! git diff --staged --exit-code --quiet; then + echo "This PR is missing some generated changes. Please update locally or merge the patch artifact." + echo "" + git diff --staged + git diff --staged > missing.patch + exit 1 + fi + name: Check for uncommitted changes + id: check-diff + if: github.event_name == 'pull_request' + + - name: Upload diff artifact + if: ${{ failure() && steps.check-diff.conclusion == 'failure' }} + uses: actions/upload-artifact@65c4c4a1ddee5b72f698fdd19549f0f0fb45cf08 # v4.6.0 + with: + name: missing.patch + path: missing.patch + + changesets: + name: changesets + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 + with: + node-version: 'lts/*' + - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + + - run: pnpm install + + - name: Check for missing changesets + run: | + PR_CHANGESETS=$(ls .changeset | (grep -v -E 'README\.md|config\.json' || true) | wc -l) + MAIN_CHANGESETS=$(git ls-tree -r origin/v2 .changeset | (grep -v -E 'README\.md|config\.json' || true) | wc -l) + + # If the PR has no changesets, but main has changesets, assume this is PR is a versioning PR and exit + if [[ $PR_CHANGESETS -eq 0 && $MAIN_CHANGESETS -gt 0 ]]; then + echo "This PR is a versioning PR, exiting" + exit 0 + fi + + # git switch -c changesets-temp + # git checkout origin/v2 -- + pnpm changeset status --since=origin/v2 + + required: + runs-on: ubuntu-latest + if: ${{ always() }} + needs: + - tests + - changesets + + steps: + - name: Check required jobs + env: + NEEDS: ${{ toJson(needs) }} + run: | + ! echo $NEEDS | jq -e 'to_entries[] | { job: .key, result: .value.result } | select(.result != "success")' diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..21251963 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,71 @@ +name: 'Code Scanning - Action' + +on: + push: + branches: + - v2 + pull_request: + branches: + - v2 + schedule: + # ┌───────────── minute (0 - 59) + # │ ┌───────────── hour (0 - 23) + # │ │ ┌───────────── day of the month (1 - 31) + # │ │ │ ┌───────────── month (1 - 12 or JAN-DEC) + # │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT) + # │ │ │ │ │ + # │ │ │ │ │ + # │ │ │ │ │ + # * * * * * + - cron: '30 1 * * 0' + +permissions: + contents: read + +# Ensure scripts are run with pipefail. See: +# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#exit-codes-and-error-action-preference +defaults: + run: + shell: bash + +jobs: + CodeQL-Build: + # CodeQL runs on ubuntu-latest, windows-latest, and macos-latest + runs-on: ubuntu-latest + if: github.repository == 'microsoft/TypeScript-Website' + + permissions: + # required for all workflows + security-events: write + + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9 + with: + config-file: ./.github/codeql/codeql-configuration.yml + # Override language selection by uncommenting this and choosing your languages + # with: + # languages: go, javascript, csharp, python, cpp, java + + # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # If this step fails, then you should remove it and run the build manually (see below). + - name: Autobuild + uses: github/codeql-action/autobuild@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9 + + # ℹ️ Command-line programs to run using the OS shell. + # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun + + # ✏️ If the Autobuild fails above, remove it and uncomment the following + # three lines and modify them (or add more) to build your code if your + # project uses a compiled language + + #- run: | + # make bootstrap + # make release + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@9e8d0789d4a0fa9ceb6b1738f7e269594bdd67f0 # v3.28.9 diff --git a/.github/workflows/deploy-prod-static.yml b/.github/workflows/deploy-prod-static.yml new file mode 100644 index 00000000..3ba369a6 --- /dev/null +++ b/.github/workflows/deploy-prod-static.yml @@ -0,0 +1,70 @@ +name: Deploy to GitHub Pages + +on: + push: + branches: + - v2 + workflow_dispatch: + schedule: + # https://crontab.guru/#0_12_*_*_1 + - cron: "0 12 * * 1" + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + # Fetch the full history, to build attribution.json + fetch-depth: 0 + - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 + with: + node-version: "18.x" + + - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + + # Builds the modules, and boostraps the other modules + - name: Build website + run: | + pnpm install + pnpm docs-sync pull microsoft/TypeScript-Website-localizations#main 1 + pnpm bootstrap + pnpm run --filter=typescriptlang-org setup-playground-cache-bust + pnpm build + + - name: Makes the site + run: | + pnpm build-site + cp -r packages/typescriptlang-org/public site + + - name: Setup Pages + uses: actions/configure-pages@983d7736d9b0ae728b81ab479565c72886d7745b # v5 + + - name: Upload artifact + uses: actions/upload-pages-artifact@56afc609e74202658d3ffba0e8f6dda462b719fa # v3 + with: + path: './site' + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4 diff --git a/.github/workflows/publish-packages.yml b/.github/workflows/publish-packages.yml new file mode 100644 index 00000000..3ba45438 --- /dev/null +++ b/.github/workflows/publish-packages.yml @@ -0,0 +1,41 @@ +name: Publish packages + +on: + push: + branches: [v2] + workflow_dispatch: + +permissions: + contents: write + pull-requests: write + +concurrency: ${{ github.workflow }}-${{ github.ref }} + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + with: + fetch-depth: 0 + filter: blob:none + token: ${{ secrets.TS_BOT_TOKEN }} + - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 + with: + node-version: "18.x" + registry-url: "https://registry.npmjs.org/" + - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + + - name: Prepare website v2 + run: | + pnpm install + pnpm bootstrap + pnpm build + + - uses: changesets/action@c8bada60c408975afd1a20b3db81d6eee6789308 # v1.4.9 + with: + publish: pnpm ci:publish + env: + GITHUB_TOKEN: ${{ secrets.TS_BOT_TOKEN }} + NODE_AUTH_TOKEN: ${{ secrets.NPM_PUBLISH_TOKEN }} diff --git a/.github/workflows/weekly-stats.yml b/.github/workflows/weekly-stats.yml new file mode 100644 index 00000000..b7911482 --- /dev/null +++ b/.github/workflows/weekly-stats.yml @@ -0,0 +1,31 @@ +name: Publish Weekly Stats + +on: + schedule: + # https://crontab.guru/#0_12_*_*_1 + - cron: "0 12 * * 1" + workflow_dispatch: + +permissions: + contents: read + comments: write + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/setup-node@1d0ff469b7ec7b3cb9d8673fde0c81c44821de2a # v4.2.0 + with: + node-version: "18.x" + + - uses: pnpm/action-setup@a7487c7e89a18df4991f7f222e4898a00d66ddda # v4.1.0 + + - run: pnpm install + + # Update the site stats + - run: node packages/typescriptlang-org/scripts/updateAppInsightsGitHubIssue.js + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + APP_INSIGHTS_API_KEY: ${{ secrets.APP_INSIGHTS_API_KEY }} + APP_INSIGHTS_ID: ${{ secrets.APP_INSIGHTS_ID }} \ No newline at end of file diff --git a/packages/documentation/copy/en/modules-reference/guides/Choosing Compiler Options.md b/packages/documentation/copy/en/modules-reference/guides/Choosing Compiler Options.md index 4bda263e..6b1f0cdc 100644 --- a/packages/documentation/copy/en/modules-reference/guides/Choosing Compiler Options.md +++ b/packages/documentation/copy/en/modules-reference/guides/Choosing Compiler Options.md @@ -142,7 +142,9 @@ Choosing compilation settings as a library author is a fundamentally different p "verbatimModuleSyntax": true, "declaration": true, "sourceMap": true, - "declarationMap": true + "declarationMap": true, + "rootDir": "src", + "outDir": "dist" } } ``` @@ -185,6 +187,7 @@ Let’s examine why we picked each of these settings: - **`verbatimModuleSyntax: true`**. This setting protects against a few module-related pitfalls that can cause problems for library consumers. First, it prevents writing any import statements that could be interpreted ambiguously based on the user’s value of `esModuleInterop` or `allowSyntheticDefaultImports`. Previously, it was often suggested that libraries compile without `esModuleInterop`, since its use in libraries could force users to adopt it too. However, it’s also possible to write imports that only work _without_ `esModuleInterop`, so neither value for the setting guarantees portability for libraries. `verbatimModuleSyntax` does provide such a guarantee.[^1] Second, it prevents the use of `export default` in modules that will be emitted as CommonJS, which can require bundler users and Node.js ESM users to consume the module differently. See the appendix on [ESM/CJS Interop](/docs/handbook/modules/appendices/esm-cjs-interop.html#library-code-needs-special-considerations) for more details. - **`declaration: true`** emits type declaration files alongside the output JavaScript. This is needed for consumers of the library to have any type information. - **`sourceMap: true`** and **`declarationMap: true`** emit source maps for the output JavaScript and type declaration files, respectively. These are only useful if the library also ships its source (`.ts`) files. By shipping source maps and source files, consumers of the library will be able to debug the library code somewhat more easily. By shipping declaration maps and source files, consumers will be able to see the original TypeScript sources when they run Go To Definition on imports from the libraries. Both of these represent a tradeoff between developer experience and library size, so it’s up to you whether to include them. +- **`rootDir: "src"`** and **`outDir: "dist"`**. Using a separate output directory is always a good idea, but it’s _necessary_ for libraries that publish their input files. Otherwise, [extension substitution](/docs/handbook/modules/reference.html#file-extension-substitution) will cause the library’s consumers to load the library’s `.ts` files instead of `.d.ts` files, causing type errors and performance problems. ### Considerations for bundling libraries @@ -205,4 +208,4 @@ If you’re using a bundler to emit your library, then all your (non-externalize A single TypeScript compilation (whether emitting or just type checking) assumes that each input file will only produce one output file. Even if `tsc` isn’t emitting anything, the type checking it performs on imported names rely on knowledge about how the output file will behave at runtime, based on the module- and emit-related options set in the tsconfig.json. While third-party emitters are generally safe to use in combination with `tsc` type checking as long as `tsc` can be configured to understand what the other emitter will emit, any solution that emits two different sets of outputs with different module formats while only type checking once leaves (at least) one of the outputs unchecked. Because external dependencies may expose different APIs to CommonJS and ESM consumers, there’s no configuration you can use to guarantee in a single compilation that both outputs will be type-safe. In practice, most dependencies follow best practices and dual-emit outputs work. Running tests and [static analysis](https://npmjs.com/package/@arethetypeswrong/cli) against all output bundles before publishing significantly reduces the chance of a serious problem going unnoticed. -[^1]: `verbatimModuleSyntax` can only work when the JS emitter emits the same module kind as `tsc` would given the tsconfig.json, source file extension, and package.json `"type"`. The option works by enforcing that the `import`/`require` written is identical to the `import`/`require` emitted. Any configuration that produces both an ESM and a CJS output from the same source file is fundamentally incompatible with `verbatimModuleSyntax`, since its whole purpose is to prevent you from writing `import` anywhere that a `require` would be emitted. `verbatimModuleSyntax` can also be defeated by configuring a third-party emitter to emit a different module kind than `tsc` would—for example, by setting `"module": "esnext"` in tsconfig.json while configuring Babel to emit CommonJS. \ No newline at end of file +[^1]: `verbatimModuleSyntax` can only work when the JS emitter emits the same module kind as `tsc` would given the tsconfig.json, source file extension, and package.json `"type"`. The option works by enforcing that the `import`/`require` written is identical to the `import`/`require` emitted. Any configuration that produces both an ESM and a CJS output from the same source file is fundamentally incompatible with `verbatimModuleSyntax`, since its whole purpose is to prevent you from writing `import` anywhere that a `require` would be emitted. `verbatimModuleSyntax` can also be defeated by configuring a third-party emitter to emit a different module kind than `tsc` would—for example, by setting `"module": "esnext"` in tsconfig.json while configuring Babel to emit CommonJS.