mirror of
https://github.com/Azure/k8s-deploy.git
synced 2026-06-22 11:29:28 +08:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6ecb006985 | |||
| d7506e9702 | |||
| 4ebf668e6f | |||
| 15920eb094 | |||
| 507f2d4fc7 | |||
| 06a06b13b9 | |||
| fa093f2922 | |||
| aabcfcba3e | |||
| fd893fd074 | |||
| 659e414483 | |||
| 1e490c6238 | |||
| 75cb5d47f7 | |||
| bcdb90f36f |
@@ -1,52 +1,51 @@
|
||||
name: "Code scanning - action"
|
||||
name: 'Code scanning - action'
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: '0 19 * * 0'
|
||||
push:
|
||||
pull_request:
|
||||
schedule:
|
||||
- cron: '0 19 * * 0'
|
||||
|
||||
jobs:
|
||||
CodeQL-Build:
|
||||
CodeQL-Build:
|
||||
# CodeQL runs on ubuntu-latest and windows-latest
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# CodeQL runs on ubuntu-latest and windows-latest
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
# with:
|
||||
# languages: go, javascript, csharp, python, cpp, java
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
# 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@v1
|
||||
# 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@v1
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
|
||||
# ✏️ 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
|
||||
# ✏️ 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
|
||||
#- run: |
|
||||
# make bootstrap
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
|
||||
@@ -1,36 +1,35 @@
|
||||
name: setting-default-labels
|
||||
|
||||
# Controls when the action will run.
|
||||
# Controls when the action will run.
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0/3 * * *"
|
||||
schedule:
|
||||
- cron: '0 0/3 * * *'
|
||||
|
||||
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
|
||||
jobs:
|
||||
build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
build:
|
||||
# The type of runner that the job will run on
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
|
||||
- uses: actions/stale@v3
|
||||
name: Setting issue as idle
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'This issue is idle because it has been open for 14 days with no activity.'
|
||||
stale-issue-label: 'idle'
|
||||
days-before-stale: 14
|
||||
days-before-close: -1
|
||||
operations-per-run: 100
|
||||
exempt-issue-labels: 'backlog'
|
||||
|
||||
- uses: actions/stale@v3
|
||||
name: Setting PR as idle
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-pr-message: 'This PR is idle because it has been open for 14 days with no activity.'
|
||||
stale-pr-label: 'idle'
|
||||
days-before-stale: 14
|
||||
days-before-close: -1
|
||||
operations-per-run: 100
|
||||
# Steps represent a sequence of tasks that will be executed as part of the job
|
||||
steps:
|
||||
- uses: actions/stale@v3
|
||||
name: Setting issue as idle
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'This issue is idle because it has been open for 14 days with no activity.'
|
||||
stale-issue-label: 'idle'
|
||||
days-before-stale: 14
|
||||
days-before-close: -1
|
||||
operations-per-run: 100
|
||||
exempt-issue-labels: 'backlog'
|
||||
|
||||
- uses: actions/stale@v3
|
||||
name: Setting PR as idle
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-pr-message: 'This PR is idle because it has been open for 14 days with no activity.'
|
||||
stale-pr-label: 'idle'
|
||||
days-before-stale: 14
|
||||
days-before-close: -1
|
||||
operations-per-run: 100
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
name: 'Run prettify'
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
prettier:
|
||||
name: Prettier Check
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Enforce Prettier
|
||||
uses: actionsx/prettier@v2
|
||||
with:
|
||||
args: --check .
|
||||
@@ -1,14 +1,14 @@
|
||||
name: Create release PR
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release:
|
||||
description: "Define release version (ex: v1, v2, v3)"
|
||||
required: true
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
release:
|
||||
description: 'Define release version (ex: v1, v2, v3)'
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
release-pr:
|
||||
uses: OliverMKing/javascript-release-workflow/.github/workflows/release-pr.yml@main
|
||||
with:
|
||||
release: ${{ github.event.inputs.release }}
|
||||
release-pr:
|
||||
uses: OliverMKing/javascript-release-workflow/.github/workflows/release-pr.yml@main
|
||||
with:
|
||||
release: ${{ github.event.inputs.release }}
|
||||
|
||||
@@ -1,215 +1,215 @@
|
||||
name: Minikube Integration Tests
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
- "releases/*"
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
- "releases/*"
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
- 'releases/*'
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- main
|
||||
- 'releases/*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
run-integration-test:
|
||||
name: Run Minikube Integration Tests
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
KUBECONFIG: /home/runner/.kube/config
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
run-integration-test:
|
||||
name: Run Minikube Integration Tests
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
KUBECONFIG: /home/runner/.kube/config
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
rm -rf node_modules/
|
||||
npm install
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
rm -rf node_modules/
|
||||
npm install
|
||||
|
||||
- name: Install ncc
|
||||
run: npm i -g @vercel/ncc
|
||||
- name: Build
|
||||
run: ncc build src/run.ts -o lib
|
||||
- name: Install ncc
|
||||
run: npm i -g @vercel/ncc
|
||||
- name: Build
|
||||
run: ncc build src/run.ts -o lib
|
||||
|
||||
- name: Set name of ns
|
||||
run: echo "::set-output name=name::$(echo `date +%Y%m%d%H%M%S`)"
|
||||
shell: bash
|
||||
id: ns
|
||||
- name: Set name of ns
|
||||
run: echo "::set-output name=name::$(echo `date +%Y%m%d%H%M%S`)"
|
||||
shell: bash
|
||||
id: ns
|
||||
|
||||
- uses: Azure/setup-kubectl@v1
|
||||
name: Install Kubectl
|
||||
- uses: Azure/setup-kubectl@v1
|
||||
name: Install Kubectl
|
||||
|
||||
- id: setup-minikube
|
||||
name: Setup Minikube
|
||||
uses: manusa/actions-setup-minikube@v2.4.2
|
||||
with:
|
||||
minikube version: "v1.24.0"
|
||||
kubernetes version: "v1.17.8"
|
||||
driver: "none"
|
||||
timeout-minutes: 3
|
||||
- id: setup-minikube
|
||||
name: Setup Minikube
|
||||
uses: manusa/actions-setup-minikube@v2.4.2
|
||||
with:
|
||||
minikube version: 'v1.24.0'
|
||||
kubernetes version: 'v1.17.8'
|
||||
driver: 'none'
|
||||
timeout-minutes: 3
|
||||
|
||||
- name: Create namespace to run tests
|
||||
run: kubectl create ns test-${{ steps.ns.outputs.name }}
|
||||
- name: Create namespace to run tests
|
||||
run: kubectl create ns test-${{ steps.ns.outputs.name }}
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- uses: actions/setup-python@v2
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Cleaning any previously created items
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'nginx-service' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'nginx-service-green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'nginx-deployment-green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'nginx-deployment' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-delete.py 'Ingress' 'nginx-ingress' ${{ steps.ns.outputs.name }}
|
||||
- name: Cleaning any previously created items
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'nginx-service' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'nginx-service-green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'nginx-deployment-green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'nginx-deployment' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-delete.py 'Ingress' 'nginx-ingress' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- name: Executing deploy action
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: service
|
||||
action: deploy
|
||||
- name: Executing deploy action
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: service
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deploments and services were created with green labels
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment-green' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'green' ${{ steps.ns.outputs.name }}
|
||||
- name: Checking if deploments and services were created with green labels
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment-green' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'green' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- name: Executing promote action
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: service
|
||||
action: promote
|
||||
- name: Executing promote action
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: service
|
||||
action: promote
|
||||
|
||||
- name: Checking if deploments and services were created with none labels after promote
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'None' ${{ steps.ns.outputs.name }}
|
||||
- name: Checking if deploments and services were created with none labels after promote
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'None' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- name: Executing deploy action on
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.19.1
|
||||
manifests: |
|
||||
test/integration/manifests/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: service
|
||||
action: deploy
|
||||
- name: Executing deploy action on
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.19.1
|
||||
manifests: |
|
||||
test/integration/manifests/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: service
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deploments and services were created with green labels, and old workloads persist on deploy
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment-green' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment' 'None' ${{ steps.ns.outputs.name }}
|
||||
- name: Checking if deploments and services were created with green labels, and old workloads persist on deploy
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment-green' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment' 'None' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- name: Executing reject action
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.19.1
|
||||
manifests: |
|
||||
test/integration/manifests/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: service
|
||||
action: reject
|
||||
- name: Executing reject action
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.19.1
|
||||
manifests: |
|
||||
test/integration/manifests/test-service.yml
|
||||
strategy: blue-green
|
||||
route-method: service
|
||||
action: reject
|
||||
|
||||
- name: Checking if deploments and services were routed back to none labels after reject
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'None' ${{ steps.ns.outputs.name }}
|
||||
- name: Checking if deploments and services were routed back to none labels after reject
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'None' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- name: Cleaning up current set up
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'nginx-service' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'nginx-deployment' ${{ steps.ns.outputs.name }}
|
||||
- name: Cleaning up current set up
|
||||
run: |
|
||||
python test/integration/k8s-deploy-delete.py 'Service' 'nginx-service' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-delete.py 'Deployment' 'nginx-deployment' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- name: Executing deploy action for ingress
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test-ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress
|
||||
action: deploy
|
||||
- name: Executing deploy action for ingress
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test-ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deploments, services and ingresses were created with green labels
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment-green' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service-green' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Ingress' 'nginx-ingress' 'green' ${{ steps.ns.outputs.name }}
|
||||
- name: Checking if deploments, services and ingresses were created with green labels
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment-green' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service-green' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Ingress' 'nginx-ingress' 'green' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- name: Executing promote action for ingress
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test-ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress
|
||||
action: promote
|
||||
- name: Executing promote action for ingress
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.14.2
|
||||
manifests: |
|
||||
test/integration/manifests/test-ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress
|
||||
action: promote
|
||||
|
||||
- name: Checking if deploments, services and ingresses were created with none labels after promote
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Ingress' 'nginx-ingress' 'None' ${{ steps.ns.outputs.name }}
|
||||
- name: Checking if deploments, services and ingresses were created with none labels after promote
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Ingress' 'nginx-ingress' 'None' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- name: Executing deploy action for ingress
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.19.1
|
||||
manifests: |
|
||||
test/integration/manifests/test-ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress
|
||||
action: deploy
|
||||
- name: Executing deploy action for ingress
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.19.1
|
||||
manifests: |
|
||||
test/integration/manifests/test-ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress
|
||||
action: deploy
|
||||
|
||||
- name: Checking if deploments, services and ingresses were created with green labels after deploy, and old deployment persists
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment-green' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service-green' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Ingress' 'nginx-ingress' 'green' ${{ steps.ns.outputs.name }}
|
||||
- name: Checking if deploments, services and ingresses were created with green labels after deploy, and old deployment persists
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment-green' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service-green' 'green' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Ingress' 'nginx-ingress' 'green' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- name: Executing reject action for ingress
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.19.1
|
||||
manifests: |
|
||||
test/integration/manifests/test-ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress
|
||||
action: reject
|
||||
- name: Executing reject action for ingress
|
||||
uses: ./
|
||||
with:
|
||||
namespace: test-${{ steps.ns.outputs.name }}
|
||||
images: nginx:1.19.1
|
||||
manifests: |
|
||||
test/integration/manifests/test-ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress
|
||||
action: reject
|
||||
|
||||
- name: Checking if deploments, services and ingresses were created with none labels after reject
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Ingress' 'nginx-ingress' 'None' ${{ steps.ns.outputs.name }}
|
||||
- name: Checking if deploments, services and ingresses were created with none labels after reject
|
||||
run: |
|
||||
python test/integration/k8s-deploy-test.py 'Deployment' 'nginx-deployment' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Service' 'nginx-service' 'None' ${{ steps.ns.outputs.name }}
|
||||
python test/integration/k8s-deploy-test.py 'Ingress' 'nginx-ingress' 'None' ${{ steps.ns.outputs.name }}
|
||||
|
||||
- if: ${{ always() }}
|
||||
name: Delete created namespace
|
||||
run: kubectl delete ns test-${{ steps.ns.outputs.name }}
|
||||
- if: ${{ always() }}
|
||||
name: Delete created namespace
|
||||
run: kubectl delete ns test-${{ steps.ns.outputs.name }}
|
||||
|
||||
- if: ${{ always() }}
|
||||
name: Posting result back to PR
|
||||
run: |
|
||||
if [ '${{ steps.job-type.outputs.type }}' == 'pr' ]; then ruby postStatus.rb ${{github.event.client_payload.repository}} ${{github.event.client_payload.commit}} ${{secrets.L2_REPO_TOKEN}} ${{job.status}} ${{github.run_id}} ${{matrix.os}} false ${{ secrets.L2_REPO_USER }}; fi
|
||||
shell: bash
|
||||
- if: ${{ always() }}
|
||||
name: Posting result back to PR
|
||||
run: |
|
||||
if [ '${{ steps.job-type.outputs.type }}' == 'pr' ]; then ruby postStatus.rb ${{github.event.client_payload.repository}} ${{github.event.client_payload.commit}} ${{secrets.L2_REPO_TOKEN}} ${{job.status}} ${{github.run_id}} ${{matrix.os}} false ${{ secrets.L2_REPO_USER }}; fi
|
||||
shell: bash
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
name: Tag and create release draft
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- releases/*
|
||||
push:
|
||||
branches:
|
||||
- releases/*
|
||||
|
||||
jobs:
|
||||
tag-and-release:
|
||||
uses: OliverMKing/javascript-release-workflow/.github/workflows/tag-and-release.yml@main
|
||||
tag-and-release:
|
||||
uses: OliverMKing/javascript-release-workflow/.github/workflows/tag-and-release.yml@main
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
name: "Run unit tests."
|
||||
name: 'Run unit tests.'
|
||||
on: # rebuild any PRs and main branch changes
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- "releases/*"
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- "releases/*"
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'releases/*'
|
||||
|
||||
jobs:
|
||||
build: # make sure build/ci works properly
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- run: |
|
||||
npm install
|
||||
npm test
|
||||
build: # make sure build/ci works properly
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- run: |
|
||||
npm install
|
||||
npm test
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# dependencies
|
||||
/node_modules
|
||||
coverage
|
||||
/lib
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"trailingComma": "none",
|
||||
"bracketSpacing": false,
|
||||
"semi": false,
|
||||
"tabWidth": 3,
|
||||
"singleQuote": true,
|
||||
"printWidth": 80
|
||||
}
|
||||
+9
-9
@@ -1,9 +1,9 @@
|
||||
# Microsoft Open Source Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
|
||||
Resources:
|
||||
|
||||
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
|
||||
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
|
||||
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
|
||||
# Microsoft Open Source Code of Conduct
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
|
||||
Resources:
|
||||
|
||||
- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/)
|
||||
- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
|
||||
- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns
|
||||
|
||||
@@ -1,434 +1,434 @@
|
||||
# Deploy manifests action for Kubernetes
|
||||
|
||||
This action is used to deploy manifests to Kubernetes clusters. It requires that the cluster context be set earlier in the workflow by using either the [Azure/aks-set-context](https://github.com/Azure/aks-set-context/tree/releases/v1) action or the [Azure/k8s-set-context](https://github.com/Azure/k8s-set-context/tree/releases/v1) action. It also requires Kubectl to be installed (you can use the [Azure/setup-kubectl](https://github.com/Azure/setup-kubectl) action).
|
||||
|
||||
If you are looking to automate your workflows to deploy to [Azure Web Apps](https://azure.microsoft.com/en-us/services/app-service/web/) and [Azure Web App for Containers](https://azure.microsoft.com/en-us/services/app-service/containers/), consider using [`Azure/webapps-deploy`](https://github.com/Azure/webapps-deploy) action.
|
||||
|
||||
## Action capabilities
|
||||
|
||||
Following are the key capabilities of this action:
|
||||
|
||||
- **Artifact substitution**: Takes a list of container images which can be specified along with their tags or digests. They are substituted into the non-templatized version of manifest files before applying to the cluster to ensure that the right version of the image is pulled by the cluster nodes.
|
||||
|
||||
- **Object stability checks**: Rollout status is checked for the Kubernetes objects deployed. This is done to incorporate stability checks while computing the action status as success/failure.
|
||||
|
||||
- **Secret handling**: The secret names specified as inputs in the action are used to augment the input manifest files with imagePullSecrets values before deploying to the cluster. Also, checkout the [Azure/k8s-create-secret](https://github.com/Azure/k8s-create-secret) action for creation of generic or docker-registry secrets in the cluster.
|
||||
|
||||
- **Deployment strategy** Supports both canary and blue-green deployment strategies
|
||||
|
||||
- **Canary strategy**: Workloads suffixed with '-baseline' and '-canary' are created. There are two methods of traffic splitting supported:
|
||||
- **Service Mesh Interface**: Service Mesh Interface abstraction allows for plug-and-play configuration with service mesh providers such as [Linkerd](https://linkerd.io/) and [Istio](https://istio.io/). Meanwhile, this action takes away the hard work of mapping SMI's TrafficSplit objects to the stable, baseline and canary services during the lifecycle of the deployment strategy. Service mesh based canary deployments using this action are more accurate as service mesh providers enable granular percentage traffic split (via service registry and sidecar containers injected into pods alongside application containers).
|
||||
- **Only Kubernetes (no service mesh)**: In the absence of service mesh, while it may not be possible to achieve exact percentage split at the request level, it is still possible to perform canary deployments by deploying -baseline and -canary workload variants next to the stable variant. The service routes requests to pods of all three workload variants as the selector-label constraints are met (KubernetesManifest will honor these when creating -baseline and -canary variants). This achieves the intended effect of routing only a portion of total requests to the canary.
|
||||
- **Blue-Green strategy**: Choosing blue-green strategy with this action leads to creation of workloads suffixed with '-green'. An identified service is one that is supplied as part of the input manifest(s) and targets a workload in the supplied manifest(s). There are three route-methods supported in the action:
|
||||
|
||||
- **Service route-method**: Identified services are configured to target the green deployments.
|
||||
- **Ingress route-method**: Along with deployments, new services are created with '-green' suffix (for identified services), and the ingresses are in turn updated to target the new services.
|
||||
- **SMI route-method**: A new [TrafficSplit](https://github.com/servicemeshinterface/smi-spec/blob/master/apis/traffic-split/v1alpha3/traffic-split.md) object is created for each identified service. The TrafficSplit object is updated to target the new deployments. This works only if SMI is set up in the cluster.
|
||||
|
||||
Traffic is routed to the new workloads only after the time provided as `version-switch-buffer` input has passed. The `promote` action creates workloads and services with new configurations but without any suffix. `reject` routes traffic back to the old workloads and deletes the '-green' workloads.
|
||||
|
||||
## Action inputs
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Action inputs</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr>
|
||||
<td>action </br></br>(Required)</td>
|
||||
<td>Acceptable values: deploy/promote/reject.</br>Promote or reject actions are used to promote or reject canary/blue-green deployments. Sample YAML snippets are provided below for guidance.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>manifests </br></br>(Required)</td>
|
||||
<td>Path to the manifest files to be used for deployment. These can also be directories containing manifest files, in which case, all manifest files in the referenced directory at every depth will be deployed. Files not ending in .yml or .yaml will be ignored.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>namespace </br></br>(Optional)
|
||||
<td>Namespace within the cluster to deploy to.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>images </br></br>(Optional)</td>
|
||||
<td>Fully qualified resource URL of the image(s) to be used for substitutions on the manifest files. This multiline input accepts specifying multiple artifact substitutions in newline separated form. For example:<br>
|
||||
<code><br>images: |<br>  contosodemo.azurecr.io/foo:test1<br>  contosodemo.azurecr.io/bar:test2<br></code><br>
|
||||
In this example, all references to contosodemo.azurecr.io/foo and contosodemo.azurecr.io/bar are searched for in the image field of the input manifest files. For the matches found, the tags test1 and test2 are substituted.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>imagepullsecrets </br></br>(Optional)</td>
|
||||
<td>Multiline input where each line contains the name of a docker-registry secret that has already been setup within the cluster. Each of these secret names are added under imagePullSecrets field for the workloads found in the input manifest files</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>pull-images</br></br>(Optional)</td>
|
||||
<td>Acceptable values: true/false</br>Default value: true</br>Switch whether to pull the images from the registry before deployment to find out Dockerfile's path in order to add it to the annotations</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>strategy </br></br>(Optional)</td>
|
||||
<td>Acceptable values: none/canary/blue-green. <br>
|
||||
Deployment strategy to be used while applying manifest files on the cluster.<br>none - No deployment strategy is used when deploying.<br>canary - Canary deployment strategy is used when deploying to the cluster.<br>blue-green - Blue-Green deployment strategy is used when deploying to cluster.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>traffic-split-method </br></br>(Optional)</td>
|
||||
<td>Acceptable values: pod/smi.<br> Default value: pod <br>SMI: Percentage traffic split is done at request level using service mesh. Service mesh has to be setup by cluster admin. Orchestration of <a href="https://github.com/servicemeshinterface/smi-spec/blob/master/apis/traffic-split/v1alpha3/traffic-split.md" data-raw-source="TrafficSplit](https://github.com/deislabs/smi-spec/blob/master/traffic-split.md)">TrafficSplit</a> objects of SMI is handled by this action. <br>Pod: Percentage split not possible at request level in the absence of service mesh. Percentage input is used to calculate the replicas for baseline and canary as a percentage of replicas specified in the input manifests for the stable variant.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>percentage </br></br>(Optional but required if strategy is canary)</td>
|
||||
<td>Used to compute the number of replicas of '-baseline' and '-canary' variants of the workloads found in manifest files. For the specified percentage input, if (percentage * numberOfDesirerdReplicas)/100 is not a round number, the floor of this number is used while creating '-baseline' and '-canary'.<br/><br/>For example, if Deployment hello-world was found in the input manifest file with 'replicas: 4' and if 'strategy: canary' and 'percentage: 25' are given as inputs to the action, then the Deployments hello-world-baseline and hello-world-canary are created with 1 replica each. The '-baseline' variant is created with the same image and tag as the stable version (4 replica variant prior to deployment) while the '-canary' variant is created with the image and tag corresponding to the new changes being deployed</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>baseline-and-canary-replicas </br></br> (Optional and relevant only if traffic-split-method is canary)</td>
|
||||
<td>The number of baseline and canary replicas. Percentage traffic split is controlled in the service mesh plane, the actual number of replicas for canary and baseline variants could be controlled independently of the traffic split. For example, assume that the input Deployment manifest desired 30 replicas to be used for stable and that the following inputs were specified for the action </br></br><code> strategy: canary<br> trafficSplitMethod: smi<br> percentage: 20<br> baselineAndCanaryReplicas: 1</code></br></br> In this case, stable variant will receive 80% traffic while baseline and canary variants will receive 10% each (20% split equally between baseline and canary). However, instead of creating baseline and canary with 3 replicas, the explicit count of baseline and canary replicas is honored. That is, only 1 replica each is created for baseline and canary variants.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>route-method </br></br>(Optional and relevant only if strategy is blue-green)</td>
|
||||
<td>Acceptable values: service/ingress/smi.</br>Default value: service.</br>Traffic is routed based on this input.
|
||||
<br>Service: Service selector labels are updated to target '-green' workloads.
|
||||
<br>Ingress: Ingress backends are updated to target the new '-green' services which in turn target '-green' deployments.
|
||||
<br>SMI: A <a href="https://github.com/servicemeshinterface/smi-spec/blob/master/apis/traffic-split/v1alpha3/traffic-split.md" data-raw-source="TrafficSplit](https://github.com/deislabs/smi-spec/blob/master/traffic-split.md)">TrafficSplit</a> object is created for each required service to route traffic to new workloads.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>version-switch-buffer </br></br>(Optional and relevant only if strategy is blue-green)</td>
|
||||
<td>Acceptable values: 1-300.</br>Default value: 0.</br>Waits for the given input in minutes before routing traffic to '-green' workloads.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>force </br></br>(Optional)</td>
|
||||
<td>Deploy when a previous deployment already exists. If true then '--force' argument is added to the apply command. Using '--force' argument is not recommended in production.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>annotate-namespace</br></br>(Optional)</td>
|
||||
<td>Acceptable values: true/false</br>Default value: true</br>Switch whether to annotate the namespace resources object or not</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic deployment (without any deployment strategy)
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: "myapp"
|
||||
manifests: |
|
||||
dir/manifestsDirectory
|
||||
images: "contoso.azurecr.io/myapp:${{ event.run_id }}"
|
||||
imagepullsecrets: |
|
||||
image-pull-secret1
|
||||
image-pull-secret2
|
||||
```
|
||||
|
||||
### Canary deployment without service mesh
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: "myapp"
|
||||
images: "contoso.azurecr.io/myapp:${{ event.run_id }}"
|
||||
imagepullsecrets: |
|
||||
image-pull-secret1
|
||||
image-pull-secret2
|
||||
manifests: |
|
||||
deployment.yaml
|
||||
service.yaml
|
||||
dir/manifestsDirectory
|
||||
strategy: canary
|
||||
action: deploy
|
||||
percentage: 20
|
||||
```
|
||||
|
||||
To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: "myapp"
|
||||
images: "contoso.azurecr.io/myapp:${{ event.run_id }}"
|
||||
imagepullsecrets: |
|
||||
image-pull-secret1
|
||||
image-pull-secret2
|
||||
manifests: |
|
||||
deployment.yaml
|
||||
service.yaml
|
||||
dir/manifestsDirectory
|
||||
strategy: canary
|
||||
action: promote # substitute reject if you want to reject
|
||||
```
|
||||
|
||||
### Canary deployment based on Service Mesh Interface
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: "myapp"
|
||||
images: "contoso.azurecr.io/myapp:${{ event.run_id }}"
|
||||
imagepullsecrets: |
|
||||
image-pull-secret1
|
||||
image-pull-secret2
|
||||
manifests: |
|
||||
deployment.yaml
|
||||
service.yaml
|
||||
dir/manifestsDirectory
|
||||
strategy: canary
|
||||
action: deploy
|
||||
traffic-split-method: smi
|
||||
percentage: 20
|
||||
baseline-and-canary-replicas: 1
|
||||
```
|
||||
|
||||
To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: "myapp"
|
||||
images: "contoso.azurecr.io/myapp:${{ event.run_id }} "
|
||||
imagepullsecrets: |
|
||||
image-pull-secret1
|
||||
image-pull-secret2
|
||||
manifests: |
|
||||
deployment.yaml
|
||||
service.yaml
|
||||
dir/manifestsDirectory
|
||||
strategy: canary
|
||||
traffic-split-method: smi
|
||||
action: reject # substitute reject if you want to reject
|
||||
```
|
||||
|
||||
### Blue-Green deployment with different route methods
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: "myapp"
|
||||
images: "contoso.azurecr.io/myapp:${{ event.run_id }}"
|
||||
imagepullsecrets: |
|
||||
image-pull-secret1
|
||||
image-pull-secret2
|
||||
manifests: |
|
||||
deployment.yaml
|
||||
service.yaml
|
||||
ingress.yml
|
||||
strategy: blue-green
|
||||
action: deploy
|
||||
route-method: ingress # substitute with service/smi as per need
|
||||
version-switch-buffer: 15
|
||||
```
|
||||
|
||||
To promote/reject the green workload created by the above snippet, the following YAML snippet could be used:
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: "myapp"
|
||||
images: "contoso.azurecr.io/myapp:${{ event.run_id }}"
|
||||
imagepullsecrets: |
|
||||
image-pull-secret1
|
||||
image-pull-secret2
|
||||
manifests: |
|
||||
deployment.yaml
|
||||
service.yaml
|
||||
ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress # should be the same as the value when action was deploy
|
||||
action: promote # substitute reject if you want to reject
|
||||
```
|
||||
|
||||
## End to end workflows
|
||||
|
||||
Following are a few examples of not just this action, but how this action could be used along with other container and k8s related actions for building images and deploying objects onto k8s clusters:
|
||||
|
||||
### Build container image and deploy to Azure Kubernetes Service cluster
|
||||
|
||||
```yaml
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
|
||||
- uses: Azure/docker-login@v1
|
||||
with:
|
||||
login-server: contoso.azurecr.io
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- run: |
|
||||
docker build . -t contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
docker push contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
|
||||
- uses: azure/setup-kubectl@v2.0
|
||||
|
||||
# Set the target AKS cluster.
|
||||
- uses: Azure/aks-set-context@v1
|
||||
with:
|
||||
creds: "${{ secrets.AZURE_CREDENTIALS }}"
|
||||
cluster-name: contoso
|
||||
resource-group: contoso-rg
|
||||
|
||||
- uses: Azure/k8s-create-secret@v1.1
|
||||
with:
|
||||
container-registry-url: contoso.azurecr.io
|
||||
container-registry-username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
secret-name: demo-k8s-secret
|
||||
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
action: deploy
|
||||
manifests: |
|
||||
manifests/deployment.yml
|
||||
manifests/service.yml
|
||||
images: |
|
||||
demo.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
imagepullsecrets: |
|
||||
demo-k8s-secret
|
||||
```
|
||||
|
||||
### Build container image and deploy to any Azure Kubernetes Service cluster
|
||||
|
||||
```yaml
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
|
||||
- uses: Azure/docker-login@v1
|
||||
with:
|
||||
login-server: contoso.azurecr.io
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- run: |
|
||||
docker build . -t contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
docker push contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
|
||||
- uses: azure/setup-kubectl@v2.0
|
||||
|
||||
- uses: Azure/k8s-set-context@v2
|
||||
with:
|
||||
kubeconfig: ${{ secrets.KUBE_CONFIG }}
|
||||
|
||||
- uses: Azure/k8s-create-secret@v1.1
|
||||
with:
|
||||
container-registry-url: contoso.azurecr.io
|
||||
container-registry-username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
secret-name: demo-k8s-secret
|
||||
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
action: deploy
|
||||
manifests: |
|
||||
manifests/deployment.yml
|
||||
manifests/service.yml
|
||||
images: |
|
||||
demo.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
imagepullsecrets: |
|
||||
demo-k8s-secret
|
||||
```
|
||||
|
||||
### Build image and add `dockerfile-path` label to it
|
||||
|
||||
We can use this image in other workflows once built.
|
||||
|
||||
```yaml
|
||||
on: [push]
|
||||
env:
|
||||
NAMESPACE: demo-ns2
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
|
||||
- uses: Azure/docker-login@v1
|
||||
with:
|
||||
login-server: contoso.azurecr.io
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- run: |
|
||||
docker build . -t contoso.azurecr.io/k8sdemo:${{ github.sha }} --label dockerfile-path=https://github.com/${{github.repo}}/blob/${{github.sha}}/Dockerfile
|
||||
docker push contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
```
|
||||
|
||||
### Use bake action to get manifests deploying to a Kubernetes cluster
|
||||
|
||||
```yaml
|
||||
on: [push]
|
||||
env:
|
||||
NAMESPACE: demo-ns2
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
|
||||
- uses: Azure/docker-login@v1
|
||||
with:
|
||||
login-server: contoso.azurecr.io
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- uses: azure/setup-kubectl@v2.0
|
||||
|
||||
# Set the target AKS cluster.
|
||||
- uses: Azure/aks-set-context@v1
|
||||
with:
|
||||
creds: "${{ secrets.AZURE_CREDENTIALS }}"
|
||||
cluster-name: contoso
|
||||
resource-group: contoso-rg
|
||||
|
||||
- uses: Azure/k8s-create-secret@v1.1
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
container-registry-url: contoso.azurecr.io
|
||||
container-registry-username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
secret-name: demo-k8s-secret
|
||||
|
||||
- uses: azure/k8s-bake@v2
|
||||
with:
|
||||
renderEngine: "helm"
|
||||
helmChart: "./aks-helloworld/"
|
||||
overrideFiles: "./aks-helloworld/values-override.yaml"
|
||||
overrides: |
|
||||
replicas:2
|
||||
helm-version: "latest"
|
||||
id: bake
|
||||
|
||||
- uses: Azure/k8s-deploy@v1.2
|
||||
with:
|
||||
action: deploy
|
||||
manifests: ${{ steps.bake.outputs.manifestsBundle }}
|
||||
images: |
|
||||
contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
imagepullsecrets: |
|
||||
demo-k8s-secret
|
||||
```
|
||||
|
||||
## Traceability Fields Support
|
||||
|
||||
- Environment variable `HELM_CHART_PATHS` is a list of helmchart files expected by k8s-deploy - it will be populated automatically if you are using k8s-bake to generate the manifests.
|
||||
- Use script to build image and add dockerfile-path label to it. The value expected is the link to the dockerfile: https://github.com/${{github.repo}}/blob/${{github.sha}}/Dockerfile. If your dockerfile is in the same repo and branch where the workflow is run, it can be a relative path and it will be converted to a link for traceability.
|
||||
- Run docker login action for each image registry - in case image build and image deploy are two distinct jobs in the same or separate workflows.
|
||||
|
||||
## Contributing
|
||||
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
||||
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
|
||||
|
||||
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
|
||||
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
|
||||
provided by the bot. You will only need to do this once across all repos using our CLA.
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
# Deploy manifests action for Kubernetes
|
||||
|
||||
This action is used to deploy manifests to Kubernetes clusters. It requires that the cluster context be set earlier in the workflow by using either the [Azure/aks-set-context](https://github.com/Azure/aks-set-context/tree/releases/v1) action or the [Azure/k8s-set-context](https://github.com/Azure/k8s-set-context/tree/releases/v1) action. It also requires Kubectl to be installed (you can use the [Azure/setup-kubectl](https://github.com/Azure/setup-kubectl) action).
|
||||
|
||||
If you are looking to automate your workflows to deploy to [Azure Web Apps](https://azure.microsoft.com/en-us/services/app-service/web/) and [Azure Web App for Containers](https://azure.microsoft.com/en-us/services/app-service/containers/), consider using [`Azure/webapps-deploy`](https://github.com/Azure/webapps-deploy) action.
|
||||
|
||||
## Action capabilities
|
||||
|
||||
Following are the key capabilities of this action:
|
||||
|
||||
- **Artifact substitution**: Takes a list of container images which can be specified along with their tags or digests. They are substituted into the non-templatized version of manifest files before applying to the cluster to ensure that the right version of the image is pulled by the cluster nodes.
|
||||
|
||||
- **Object stability checks**: Rollout status is checked for the Kubernetes objects deployed. This is done to incorporate stability checks while computing the action status as success/failure.
|
||||
|
||||
- **Secret handling**: The secret names specified as inputs in the action are used to augment the input manifest files with imagePullSecrets values before deploying to the cluster. Also, checkout the [Azure/k8s-create-secret](https://github.com/Azure/k8s-create-secret) action for creation of generic or docker-registry secrets in the cluster.
|
||||
|
||||
- **Deployment strategy** Supports both canary and blue-green deployment strategies
|
||||
|
||||
- **Canary strategy**: Workloads suffixed with '-baseline' and '-canary' are created. There are two methods of traffic splitting supported:
|
||||
- **Service Mesh Interface**: Service Mesh Interface abstraction allows for plug-and-play configuration with service mesh providers such as [Linkerd](https://linkerd.io/) and [Istio](https://istio.io/). Meanwhile, this action takes away the hard work of mapping SMI's TrafficSplit objects to the stable, baseline and canary services during the lifecycle of the deployment strategy. Service mesh based canary deployments using this action are more accurate as service mesh providers enable granular percentage traffic split (via service registry and sidecar containers injected into pods alongside application containers).
|
||||
- **Only Kubernetes (no service mesh)**: In the absence of service mesh, while it may not be possible to achieve exact percentage split at the request level, it is still possible to perform canary deployments by deploying -baseline and -canary workload variants next to the stable variant. The service routes requests to pods of all three workload variants as the selector-label constraints are met (KubernetesManifest will honor these when creating -baseline and -canary variants). This achieves the intended effect of routing only a portion of total requests to the canary.
|
||||
- **Blue-Green strategy**: Choosing blue-green strategy with this action leads to creation of workloads suffixed with '-green'. An identified service is one that is supplied as part of the input manifest(s) and targets a workload in the supplied manifest(s). There are three route-methods supported in the action:
|
||||
|
||||
- **Service route-method**: Identified services are configured to target the green deployments.
|
||||
- **Ingress route-method**: Along with deployments, new services are created with '-green' suffix (for identified services), and the ingresses are in turn updated to target the new services.
|
||||
- **SMI route-method**: A new [TrafficSplit](https://github.com/servicemeshinterface/smi-spec/blob/master/apis/traffic-split/v1alpha3/traffic-split.md) object is created for each identified service. The TrafficSplit object is updated to target the new deployments. This works only if SMI is set up in the cluster.
|
||||
|
||||
Traffic is routed to the new workloads only after the time provided as `version-switch-buffer` input has passed. The `promote` action creates workloads and services with new configurations but without any suffix. `reject` routes traffic back to the old workloads and deletes the '-green' workloads.
|
||||
|
||||
## Action inputs
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Action inputs</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr>
|
||||
<td>action </br></br>(Required)</td>
|
||||
<td>Acceptable values: deploy/promote/reject.</br>Promote or reject actions are used to promote or reject canary/blue-green deployments. Sample YAML snippets are provided below for guidance.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>manifests </br></br>(Required)</td>
|
||||
<td>Path to the manifest files to be used for deployment. These can also be directories containing manifest files, in which case, all manifest files in the referenced directory at every depth will be deployed. Files not ending in .yml or .yaml will be ignored.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>namespace </br></br>(Optional)
|
||||
<td>Namespace within the cluster to deploy to.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>images </br></br>(Optional)</td>
|
||||
<td>Fully qualified resource URL of the image(s) to be used for substitutions on the manifest files. This multiline input accepts specifying multiple artifact substitutions in newline separated form. For example:<br>
|
||||
<code><br>images: |<br>  contosodemo.azurecr.io/foo:test1<br>  contosodemo.azurecr.io/bar:test2<br></code><br>
|
||||
In this example, all references to contosodemo.azurecr.io/foo and contosodemo.azurecr.io/bar are searched for in the image field of the input manifest files. For the matches found, the tags test1 and test2 are substituted.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>imagepullsecrets </br></br>(Optional)</td>
|
||||
<td>Multiline input where each line contains the name of a docker-registry secret that has already been setup within the cluster. Each of these secret names are added under imagePullSecrets field for the workloads found in the input manifest files</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>pull-images</br></br>(Optional)</td>
|
||||
<td>Acceptable values: true/false</br>Default value: true</br>Switch whether to pull the images from the registry before deployment to find out Dockerfile's path in order to add it to the annotations</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>strategy </br></br>(Optional)</td>
|
||||
<td>Acceptable values: none/canary/blue-green. <br>
|
||||
Deployment strategy to be used while applying manifest files on the cluster.<br>none - No deployment strategy is used when deploying.<br>canary - Canary deployment strategy is used when deploying to the cluster.<br>blue-green - Blue-Green deployment strategy is used when deploying to cluster.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>traffic-split-method </br></br>(Optional)</td>
|
||||
<td>Acceptable values: pod/smi.<br> Default value: pod <br>SMI: Percentage traffic split is done at request level using service mesh. Service mesh has to be setup by cluster admin. Orchestration of <a href="https://github.com/servicemeshinterface/smi-spec/blob/master/apis/traffic-split/v1alpha3/traffic-split.md" data-raw-source="TrafficSplit](https://github.com/deislabs/smi-spec/blob/master/traffic-split.md)">TrafficSplit</a> objects of SMI is handled by this action. <br>Pod: Percentage split not possible at request level in the absence of service mesh. Percentage input is used to calculate the replicas for baseline and canary as a percentage of replicas specified in the input manifests for the stable variant.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>percentage </br></br>(Optional but required if strategy is canary)</td>
|
||||
<td>Used to compute the number of replicas of '-baseline' and '-canary' variants of the workloads found in manifest files. For the specified percentage input, if (percentage * numberOfDesirerdReplicas)/100 is not a round number, the floor of this number is used while creating '-baseline' and '-canary'.<br/><br/>For example, if Deployment hello-world was found in the input manifest file with 'replicas: 4' and if 'strategy: canary' and 'percentage: 25' are given as inputs to the action, then the Deployments hello-world-baseline and hello-world-canary are created with 1 replica each. The '-baseline' variant is created with the same image and tag as the stable version (4 replica variant prior to deployment) while the '-canary' variant is created with the image and tag corresponding to the new changes being deployed</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>baseline-and-canary-replicas </br></br> (Optional and relevant only if traffic-split-method is canary)</td>
|
||||
<td>The number of baseline and canary replicas. Percentage traffic split is controlled in the service mesh plane, the actual number of replicas for canary and baseline variants could be controlled independently of the traffic split. For example, assume that the input Deployment manifest desired 30 replicas to be used for stable and that the following inputs were specified for the action </br></br><code> strategy: canary<br> trafficSplitMethod: smi<br> percentage: 20<br> baselineAndCanaryReplicas: 1</code></br></br> In this case, stable variant will receive 80% traffic while baseline and canary variants will receive 10% each (20% split equally between baseline and canary). However, instead of creating baseline and canary with 3 replicas, the explicit count of baseline and canary replicas is honored. That is, only 1 replica each is created for baseline and canary variants.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>route-method </br></br>(Optional and relevant only if strategy is blue-green)</td>
|
||||
<td>Acceptable values: service/ingress/smi.</br>Default value: service.</br>Traffic is routed based on this input.
|
||||
<br>Service: Service selector labels are updated to target '-green' workloads.
|
||||
<br>Ingress: Ingress backends are updated to target the new '-green' services which in turn target '-green' deployments.
|
||||
<br>SMI: A <a href="https://github.com/servicemeshinterface/smi-spec/blob/master/apis/traffic-split/v1alpha3/traffic-split.md" data-raw-source="TrafficSplit](https://github.com/deislabs/smi-spec/blob/master/traffic-split.md)">TrafficSplit</a> object is created for each required service to route traffic to new workloads.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>version-switch-buffer </br></br>(Optional and relevant only if strategy is blue-green)</td>
|
||||
<td>Acceptable values: 1-300.</br>Default value: 0.</br>Waits for the given input in minutes before routing traffic to '-green' workloads.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>force </br></br>(Optional)</td>
|
||||
<td>Deploy when a previous deployment already exists. If true then '--force' argument is added to the apply command. Using '--force' argument is not recommended in production.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>annotate-namespace</br></br>(Optional)</td>
|
||||
<td>Acceptable values: true/false</br>Default value: true</br>Switch whether to annotate the namespace resources object or not</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic deployment (without any deployment strategy)
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
manifests: |
|
||||
dir/manifestsDirectory
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||
imagepullsecrets: |
|
||||
image-pull-secret1
|
||||
image-pull-secret2
|
||||
```
|
||||
|
||||
### Canary deployment without service mesh
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||
imagepullsecrets: |
|
||||
image-pull-secret1
|
||||
image-pull-secret2
|
||||
manifests: |
|
||||
deployment.yaml
|
||||
service.yaml
|
||||
dir/manifestsDirectory
|
||||
strategy: canary
|
||||
action: deploy
|
||||
percentage: 20
|
||||
```
|
||||
|
||||
To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||
imagepullsecrets: |
|
||||
image-pull-secret1
|
||||
image-pull-secret2
|
||||
manifests: |
|
||||
deployment.yaml
|
||||
service.yaml
|
||||
dir/manifestsDirectory
|
||||
strategy: canary
|
||||
action: promote # substitute reject if you want to reject
|
||||
```
|
||||
|
||||
### Canary deployment based on Service Mesh Interface
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||
imagepullsecrets: |
|
||||
image-pull-secret1
|
||||
image-pull-secret2
|
||||
manifests: |
|
||||
deployment.yaml
|
||||
service.yaml
|
||||
dir/manifestsDirectory
|
||||
strategy: canary
|
||||
action: deploy
|
||||
traffic-split-method: smi
|
||||
percentage: 20
|
||||
baseline-and-canary-replicas: 1
|
||||
```
|
||||
|
||||
To promote/reject the canary created by the above snippet, the following YAML snippet could be used:
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }} '
|
||||
imagepullsecrets: |
|
||||
image-pull-secret1
|
||||
image-pull-secret2
|
||||
manifests: |
|
||||
deployment.yaml
|
||||
service.yaml
|
||||
dir/manifestsDirectory
|
||||
strategy: canary
|
||||
traffic-split-method: smi
|
||||
action: reject # substitute reject if you want to reject
|
||||
```
|
||||
|
||||
### Blue-Green deployment with different route methods
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||
imagepullsecrets: |
|
||||
image-pull-secret1
|
||||
image-pull-secret2
|
||||
manifests: |
|
||||
deployment.yaml
|
||||
service.yaml
|
||||
ingress.yml
|
||||
strategy: blue-green
|
||||
action: deploy
|
||||
route-method: ingress # substitute with service/smi as per need
|
||||
version-switch-buffer: 15
|
||||
```
|
||||
|
||||
To promote/reject the green workload created by the above snippet, the following YAML snippet could be used:
|
||||
|
||||
```yaml
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
namespace: 'myapp'
|
||||
images: 'contoso.azurecr.io/myapp:${{ event.run_id }}'
|
||||
imagepullsecrets: |
|
||||
image-pull-secret1
|
||||
image-pull-secret2
|
||||
manifests: |
|
||||
deployment.yaml
|
||||
service.yaml
|
||||
ingress.yml
|
||||
strategy: blue-green
|
||||
route-method: ingress # should be the same as the value when action was deploy
|
||||
action: promote # substitute reject if you want to reject
|
||||
```
|
||||
|
||||
## End to end workflows
|
||||
|
||||
Following are a few examples of not just this action, but how this action could be used along with other container and k8s related actions for building images and deploying objects onto k8s clusters:
|
||||
|
||||
### Build container image and deploy to Azure Kubernetes Service cluster
|
||||
|
||||
```yaml
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
|
||||
- uses: Azure/docker-login@v1
|
||||
with:
|
||||
login-server: contoso.azurecr.io
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- run: |
|
||||
docker build . -t contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
docker push contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
|
||||
- uses: azure/setup-kubectl@v2.0
|
||||
|
||||
# Set the target AKS cluster.
|
||||
- uses: Azure/aks-set-context@v1
|
||||
with:
|
||||
creds: '${{ secrets.AZURE_CREDENTIALS }}'
|
||||
cluster-name: contoso
|
||||
resource-group: contoso-rg
|
||||
|
||||
- uses: Azure/k8s-create-secret@v1.1
|
||||
with:
|
||||
container-registry-url: contoso.azurecr.io
|
||||
container-registry-username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
secret-name: demo-k8s-secret
|
||||
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
action: deploy
|
||||
manifests: |
|
||||
manifests/deployment.yml
|
||||
manifests/service.yml
|
||||
images: |
|
||||
demo.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
imagepullsecrets: |
|
||||
demo-k8s-secret
|
||||
```
|
||||
|
||||
### Build container image and deploy to any Azure Kubernetes Service cluster
|
||||
|
||||
```yaml
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
|
||||
- uses: Azure/docker-login@v1
|
||||
with:
|
||||
login-server: contoso.azurecr.io
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- run: |
|
||||
docker build . -t contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
docker push contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
|
||||
- uses: azure/setup-kubectl@v2.0
|
||||
|
||||
- uses: Azure/k8s-set-context@v2
|
||||
with:
|
||||
kubeconfig: ${{ secrets.KUBE_CONFIG }}
|
||||
|
||||
- uses: Azure/k8s-create-secret@v1.1
|
||||
with:
|
||||
container-registry-url: contoso.azurecr.io
|
||||
container-registry-username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
secret-name: demo-k8s-secret
|
||||
|
||||
- uses: Azure/k8s-deploy@v3.1
|
||||
with:
|
||||
action: deploy
|
||||
manifests: |
|
||||
manifests/deployment.yml
|
||||
manifests/service.yml
|
||||
images: |
|
||||
demo.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
imagepullsecrets: |
|
||||
demo-k8s-secret
|
||||
```
|
||||
|
||||
### Build image and add `dockerfile-path` label to it
|
||||
|
||||
We can use this image in other workflows once built.
|
||||
|
||||
```yaml
|
||||
on: [push]
|
||||
env:
|
||||
NAMESPACE: demo-ns2
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
|
||||
- uses: Azure/docker-login@v1
|
||||
with:
|
||||
login-server: contoso.azurecr.io
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- run: |
|
||||
docker build . -t contoso.azurecr.io/k8sdemo:${{ github.sha }} --label dockerfile-path=https://github.com/${{github.repo}}/blob/${{github.sha}}/Dockerfile
|
||||
docker push contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
```
|
||||
|
||||
### Use bake action to get manifests deploying to a Kubernetes cluster
|
||||
|
||||
```yaml
|
||||
on: [push]
|
||||
env:
|
||||
NAMESPACE: demo-ns2
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@master
|
||||
|
||||
- uses: Azure/docker-login@v1
|
||||
with:
|
||||
login-server: contoso.azurecr.io
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- uses: azure/setup-kubectl@v2.0
|
||||
|
||||
# Set the target AKS cluster.
|
||||
- uses: Azure/aks-set-context@v1
|
||||
with:
|
||||
creds: '${{ secrets.AZURE_CREDENTIALS }}'
|
||||
cluster-name: contoso
|
||||
resource-group: contoso-rg
|
||||
|
||||
- uses: Azure/k8s-create-secret@v1.1
|
||||
with:
|
||||
namespace: ${{ env.NAMESPACE }}
|
||||
container-registry-url: contoso.azurecr.io
|
||||
container-registry-username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
container-registry-password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
secret-name: demo-k8s-secret
|
||||
|
||||
- uses: azure/k8s-bake@v2
|
||||
with:
|
||||
renderEngine: 'helm'
|
||||
helmChart: './aks-helloworld/'
|
||||
overrideFiles: './aks-helloworld/values-override.yaml'
|
||||
overrides: |
|
||||
replicas:2
|
||||
helm-version: 'latest'
|
||||
id: bake
|
||||
|
||||
- uses: Azure/k8s-deploy@v1.2
|
||||
with:
|
||||
action: deploy
|
||||
manifests: ${{ steps.bake.outputs.manifestsBundle }}
|
||||
images: |
|
||||
contoso.azurecr.io/k8sdemo:${{ github.sha }}
|
||||
imagepullsecrets: |
|
||||
demo-k8s-secret
|
||||
```
|
||||
|
||||
## Traceability Fields Support
|
||||
|
||||
- Environment variable `HELM_CHART_PATHS` is a list of helmchart files expected by k8s-deploy - it will be populated automatically if you are using k8s-bake to generate the manifests.
|
||||
- Use script to build image and add dockerfile-path label to it. The value expected is the link to the dockerfile: https://github.com/${{github.repo}}/blob/${{github.sha}}/Dockerfile. If your dockerfile is in the same repo and branch where the workflow is run, it can be a relative path and it will be converted to a link for traceability.
|
||||
- Run docker login action for each image registry - in case image build and image deploy are two distinct jobs in the same or separate workflows.
|
||||
|
||||
## Contributing
|
||||
|
||||
This project welcomes contributions and suggestions. Most contributions require you to agree to a
|
||||
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
|
||||
the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com.
|
||||
|
||||
When you submit a pull request, a CLA bot will automatically determine whether you need to provide
|
||||
a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions
|
||||
provided by the bot. You will only need to do this once across all repos using our CLA.
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or
|
||||
contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
||||
|
||||
+35
-35
@@ -1,35 +1,35 @@
|
||||
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.1 BLOCK -->
|
||||
|
||||
## Security
|
||||
|
||||
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [many more](https://opensource.microsoft.com/).
|
||||
|
||||
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [definition](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below.
|
||||
|
||||
## Reporting Security Issues
|
||||
|
||||
**Please do not report security vulnerabilities through public GitHub issues.** Instead, please report them to the Microsoft Security Response Center at [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://technet.microsoft.com/en-us/security/dn606155).
|
||||
|
||||
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
|
||||
|
||||
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
|
||||
|
||||
* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
||||
* Full paths of source file(s) related to the manifestation of the issue
|
||||
* The location of the affected source code (tag/branch/commit or direct URL)
|
||||
* Any special configuration required to reproduce the issue
|
||||
* Step-by-step instructions to reproduce the issue
|
||||
* Proof-of-concept or exploit code (if possible)
|
||||
* Impact of the issue, including how an attacker might exploit the issue
|
||||
|
||||
This information will help us triage your report more quickly.
|
||||
|
||||
## Preferred Languages
|
||||
|
||||
We prefer all communications to be in English.
|
||||
|
||||
## Policy
|
||||
|
||||
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
|
||||
|
||||
<!-- END MICROSOFT SECURITY.MD BLOCK -->
|
||||
<!-- BEGIN MICROSOFT SECURITY.MD V0.0.1 BLOCK -->
|
||||
|
||||
## Security
|
||||
|
||||
Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [many more](https://opensource.microsoft.com/).
|
||||
|
||||
If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [definition](<https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)>) of a security vulnerability, please report it to us as described below.
|
||||
|
||||
## Reporting Security Issues
|
||||
|
||||
**Please do not report security vulnerabilities through public GitHub issues.** Instead, please report them to the Microsoft Security Response Center at [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://technet.microsoft.com/en-us/security/dn606155).
|
||||
|
||||
You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
|
||||
|
||||
Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
|
||||
|
||||
- Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
|
||||
- Full paths of source file(s) related to the manifestation of the issue
|
||||
- The location of the affected source code (tag/branch/commit or direct URL)
|
||||
- Any special configuration required to reproduce the issue
|
||||
- Step-by-step instructions to reproduce the issue
|
||||
- Proof-of-concept or exploit code (if possible)
|
||||
- Impact of the issue, including how an attacker might exploit the issue
|
||||
|
||||
This information will help us triage your report more quickly.
|
||||
|
||||
## Preferred Languages
|
||||
|
||||
We prefer all communications to be in English.
|
||||
|
||||
## Policy
|
||||
|
||||
Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
|
||||
|
||||
<!-- END MICROSOFT SECURITY.MD BLOCK -->
|
||||
|
||||
+67
-67
@@ -1,67 +1,67 @@
|
||||
name: "Deploy to Kubernetes cluster"
|
||||
description: "Deploy to a Kubernetes cluster including, but not limited to Azure Kubernetes Service (AKS) clusters"
|
||||
inputs:
|
||||
# Please ensure you have used either azure/k8s-actions/aks-set-context or azure/k8s-actions/k8s-set-context in the workflow before this action
|
||||
# You also need to have kubectl installed (azure/setup-kubectl)
|
||||
namespace:
|
||||
description: "Choose the target Kubernetes namespace. If the namespace is not provided, the commands will run in the default namespace."
|
||||
required: false
|
||||
manifests:
|
||||
description: "Path to the manifest files which will be used for deployment."
|
||||
required: true
|
||||
images:
|
||||
description: "Fully qualified resource URL of the image(s) to be used for substitutions on the manifest files Example: contosodemo.azurecr.io/helloworld:test"
|
||||
required: false
|
||||
imagepullsecrets:
|
||||
description: "Name of a docker-registry secret that has already been set up within the cluster. Each of these secret names are added under imagePullSecrets field for the workloads found in the input manifest files"
|
||||
required: false
|
||||
pull-images:
|
||||
description: "Switch whether to pull the images from the registry before deployment to find out Dockerfile's path in order to add it to the annotations"
|
||||
required: false
|
||||
default: true
|
||||
strategy:
|
||||
description: "Deployment strategy to be used. Allowed values are none, canary and blue-green"
|
||||
required: false
|
||||
default: "none"
|
||||
route-method:
|
||||
description: "Route based on service, ingress or SMI for blue-green strategy"
|
||||
required: false
|
||||
default: "service"
|
||||
version-switch-buffer:
|
||||
description: "Indicates the buffer time in minutes before the switch is made to the green version (max is 300 min ie. 5hrs)"
|
||||
required: false
|
||||
default: 0
|
||||
traffic-split-method:
|
||||
description: "Traffic split method to be used. Allowed values are pod and smi"
|
||||
required: false
|
||||
default: "pod"
|
||||
baseline-and-canary-replicas:
|
||||
description: "Baseline and canary replicas count. Valid value between 0 to 100 (inclusive)"
|
||||
required: false
|
||||
default: 0
|
||||
percentage:
|
||||
description: "Percentage of traffic redirect to canary deployment"
|
||||
required: false
|
||||
default: 0
|
||||
action:
|
||||
description: "deploy, promote, or reject"
|
||||
required: true
|
||||
default: "deploy"
|
||||
force:
|
||||
description: "Deploy when a previous deployment already exists. If true then --force argument is added to the apply command"
|
||||
required: false
|
||||
default: false
|
||||
token:
|
||||
description: "Github token"
|
||||
default: ${{ github.token }}
|
||||
required: true
|
||||
annotate-namespace:
|
||||
description: "Annotate the target namespace"
|
||||
required: false
|
||||
default: true
|
||||
|
||||
branding:
|
||||
color: "green"
|
||||
runs:
|
||||
using: "node12"
|
||||
main: "lib/index.js"
|
||||
name: 'Deploy to Kubernetes cluster'
|
||||
description: 'Deploy to a Kubernetes cluster including, but not limited to Azure Kubernetes Service (AKS) clusters'
|
||||
inputs:
|
||||
# Please ensure you have used either azure/k8s-actions/aks-set-context or azure/k8s-actions/k8s-set-context in the workflow before this action
|
||||
# You also need to have kubectl installed (azure/setup-kubectl)
|
||||
namespace:
|
||||
description: 'Choose the target Kubernetes namespace. If the namespace is not provided, the commands will run in the default namespace.'
|
||||
required: false
|
||||
manifests:
|
||||
description: 'Path to the manifest files which will be used for deployment.'
|
||||
required: true
|
||||
images:
|
||||
description: 'Fully qualified resource URL of the image(s) to be used for substitutions on the manifest files Example: contosodemo.azurecr.io/helloworld:test'
|
||||
required: false
|
||||
imagepullsecrets:
|
||||
description: 'Name of a docker-registry secret that has already been set up within the cluster. Each of these secret names are added under imagePullSecrets field for the workloads found in the input manifest files'
|
||||
required: false
|
||||
pull-images:
|
||||
description: "Switch whether to pull the images from the registry before deployment to find out Dockerfile's path in order to add it to the annotations"
|
||||
required: false
|
||||
default: true
|
||||
strategy:
|
||||
description: 'Deployment strategy to be used. Allowed values are none, canary and blue-green'
|
||||
required: false
|
||||
default: 'none'
|
||||
route-method:
|
||||
description: 'Route based on service, ingress or SMI for blue-green strategy'
|
||||
required: false
|
||||
default: 'service'
|
||||
version-switch-buffer:
|
||||
description: 'Indicates the buffer time in minutes before the switch is made to the green version (max is 300 min ie. 5hrs)'
|
||||
required: false
|
||||
default: 0
|
||||
traffic-split-method:
|
||||
description: 'Traffic split method to be used. Allowed values are pod and smi'
|
||||
required: false
|
||||
default: 'pod'
|
||||
baseline-and-canary-replicas:
|
||||
description: 'Baseline and canary replicas count. Valid value between 0 to 100 (inclusive)'
|
||||
required: false
|
||||
default: 0
|
||||
percentage:
|
||||
description: 'Percentage of traffic redirect to canary deployment'
|
||||
required: false
|
||||
default: 0
|
||||
action:
|
||||
description: 'deploy, promote, or reject'
|
||||
required: true
|
||||
default: 'deploy'
|
||||
force:
|
||||
description: 'Deploy when a previous deployment already exists. If true then --force argument is added to the apply command'
|
||||
required: false
|
||||
default: false
|
||||
token:
|
||||
description: 'Github token'
|
||||
default: ${{ github.token }}
|
||||
required: true
|
||||
annotate-namespace:
|
||||
description: 'Annotate the target namespace'
|
||||
required: false
|
||||
default: true
|
||||
|
||||
branding:
|
||||
color: 'green'
|
||||
runs:
|
||||
using: 'node16'
|
||||
main: 'lib/index.js'
|
||||
|
||||
+8
-8
@@ -1,10 +1,10 @@
|
||||
module.exports = {
|
||||
clearMocks: true,
|
||||
moduleFileExtensions: ['js', 'ts'],
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/*.test.ts'],
|
||||
transform: {
|
||||
clearMocks: true,
|
||||
moduleFileExtensions: ['js', 'ts'],
|
||||
testEnvironment: 'node',
|
||||
testMatch: ['**/*.test.ts'],
|
||||
transform: {
|
||||
'^.+\\.ts$': 'ts-jest'
|
||||
},
|
||||
verbose: true
|
||||
}
|
||||
},
|
||||
verbose: true
|
||||
}
|
||||
|
||||
+291
-269
File diff suppressed because it is too large
Load Diff
+6108
-6075
File diff suppressed because it is too large
Load Diff
-47
@@ -1,47 +0,0 @@
|
||||
Copyright jQuery Foundation and other contributors <https://jquery.org/>
|
||||
|
||||
Based on Underscore.js, copyright Jeremy Ashkenas,
|
||||
DocumentCloud and Investigative Reporters & Editors <http://underscorejs.org/>
|
||||
|
||||
This software consists of voluntary contributions made by many
|
||||
individuals. For exact contribution history, see the revision history
|
||||
available at https://github.com/lodash/lodash
|
||||
|
||||
The following license applies to all parts of this software except as
|
||||
documented below:
|
||||
|
||||
====
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
====
|
||||
|
||||
Copyright and related rights for sample code are waived via CC0. Sample
|
||||
code is defined as all source code displayed within the prose of the
|
||||
documentation.
|
||||
|
||||
CC0: http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
====
|
||||
|
||||
Files located in the node_modules and vendor directories are externally
|
||||
maintained libraries used by this software which have their own
|
||||
licenses; we recommend you read them, as their terms may differ from the
|
||||
terms above.
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
# lodash.memoize v4.1.2
|
||||
|
||||
The [lodash](https://lodash.com/) method `_.memoize` exported as a [Node.js](https://nodejs.org/) module.
|
||||
|
||||
## Installation
|
||||
|
||||
Using npm:
|
||||
```bash
|
||||
$ {sudo -H} npm i -g npm
|
||||
$ npm i --save lodash.memoize
|
||||
```
|
||||
|
||||
In Node.js:
|
||||
```js
|
||||
var memoize = require('lodash.memoize');
|
||||
```
|
||||
|
||||
See the [documentation](https://lodash.com/docs#memoize) or [package source](https://github.com/lodash/lodash/blob/4.1.2-npm-packages/lodash.memoize) for more details.
|
||||
-676
@@ -1,676 +0,0 @@
|
||||
/**
|
||||
* lodash (Custom Build) <https://lodash.com/>
|
||||
* Build: `lodash modularize exports="npm" -o ./`
|
||||
* Copyright jQuery Foundation and other contributors <https://jquery.org/>
|
||||
* Released under MIT license <https://lodash.com/license>
|
||||
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
|
||||
* Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
|
||||
*/
|
||||
|
||||
/** Used as the `TypeError` message for "Functions" methods. */
|
||||
var FUNC_ERROR_TEXT = 'Expected a function';
|
||||
|
||||
/** Used to stand-in for `undefined` hash values. */
|
||||
var HASH_UNDEFINED = '__lodash_hash_undefined__';
|
||||
|
||||
/** `Object#toString` result references. */
|
||||
var funcTag = '[object Function]',
|
||||
genTag = '[object GeneratorFunction]';
|
||||
|
||||
/**
|
||||
* Used to match `RegExp`
|
||||
* [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
|
||||
*/
|
||||
var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
|
||||
|
||||
/** Used to detect host constructors (Safari). */
|
||||
var reIsHostCtor = /^\[object .+?Constructor\]$/;
|
||||
|
||||
/** Detect free variable `global` from Node.js. */
|
||||
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
|
||||
|
||||
/** Detect free variable `self`. */
|
||||
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
|
||||
|
||||
/** Used as a reference to the global object. */
|
||||
var root = freeGlobal || freeSelf || Function('return this')();
|
||||
|
||||
/**
|
||||
* Gets the value at `key` of `object`.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} [object] The object to query.
|
||||
* @param {string} key The key of the property to get.
|
||||
* @returns {*} Returns the property value.
|
||||
*/
|
||||
function getValue(object, key) {
|
||||
return object == null ? undefined : object[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if `value` is a host object in IE < 9.
|
||||
*
|
||||
* @private
|
||||
* @param {*} value The value to check.
|
||||
* @returns {boolean} Returns `true` if `value` is a host object, else `false`.
|
||||
*/
|
||||
function isHostObject(value) {
|
||||
// Many host objects are `Object` objects that can coerce to strings
|
||||
// despite having improperly defined `toString` methods.
|
||||
var result = false;
|
||||
if (value != null && typeof value.toString != 'function') {
|
||||
try {
|
||||
result = !!(value + '');
|
||||
} catch (e) {}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Used for built-in method references. */
|
||||
var arrayProto = Array.prototype,
|
||||
funcProto = Function.prototype,
|
||||
objectProto = Object.prototype;
|
||||
|
||||
/** Used to detect overreaching core-js shims. */
|
||||
var coreJsData = root['__core-js_shared__'];
|
||||
|
||||
/** Used to detect methods masquerading as native. */
|
||||
var maskSrcKey = (function() {
|
||||
var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');
|
||||
return uid ? ('Symbol(src)_1.' + uid) : '';
|
||||
}());
|
||||
|
||||
/** Used to resolve the decompiled source of functions. */
|
||||
var funcToString = funcProto.toString;
|
||||
|
||||
/** Used to check objects for own properties. */
|
||||
var hasOwnProperty = objectProto.hasOwnProperty;
|
||||
|
||||
/**
|
||||
* Used to resolve the
|
||||
* [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
|
||||
* of values.
|
||||
*/
|
||||
var objectToString = objectProto.toString;
|
||||
|
||||
/** Used to detect if a method is native. */
|
||||
var reIsNative = RegExp('^' +
|
||||
funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
|
||||
.replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
|
||||
);
|
||||
|
||||
/** Built-in value references. */
|
||||
var splice = arrayProto.splice;
|
||||
|
||||
/* Built-in method references that are verified to be native. */
|
||||
var Map = getNative(root, 'Map'),
|
||||
nativeCreate = getNative(Object, 'create');
|
||||
|
||||
/**
|
||||
* Creates a hash object.
|
||||
*
|
||||
* @private
|
||||
* @constructor
|
||||
* @param {Array} [entries] The key-value pairs to cache.
|
||||
*/
|
||||
function Hash(entries) {
|
||||
var index = -1,
|
||||
length = entries ? entries.length : 0;
|
||||
|
||||
this.clear();
|
||||
while (++index < length) {
|
||||
var entry = entries[index];
|
||||
this.set(entry[0], entry[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all key-value entries from the hash.
|
||||
*
|
||||
* @private
|
||||
* @name clear
|
||||
* @memberOf Hash
|
||||
*/
|
||||
function hashClear() {
|
||||
this.__data__ = nativeCreate ? nativeCreate(null) : {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes `key` and its value from the hash.
|
||||
*
|
||||
* @private
|
||||
* @name delete
|
||||
* @memberOf Hash
|
||||
* @param {Object} hash The hash to modify.
|
||||
* @param {string} key The key of the value to remove.
|
||||
* @returns {boolean} Returns `true` if the entry was removed, else `false`.
|
||||
*/
|
||||
function hashDelete(key) {
|
||||
return this.has(key) && delete this.__data__[key];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hash value for `key`.
|
||||
*
|
||||
* @private
|
||||
* @name get
|
||||
* @memberOf Hash
|
||||
* @param {string} key The key of the value to get.
|
||||
* @returns {*} Returns the entry value.
|
||||
*/
|
||||
function hashGet(key) {
|
||||
var data = this.__data__;
|
||||
if (nativeCreate) {
|
||||
var result = data[key];
|
||||
return result === HASH_UNDEFINED ? undefined : result;
|
||||
}
|
||||
return hasOwnProperty.call(data, key) ? data[key] : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a hash value for `key` exists.
|
||||
*
|
||||
* @private
|
||||
* @name has
|
||||
* @memberOf Hash
|
||||
* @param {string} key The key of the entry to check.
|
||||
* @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
|
||||
*/
|
||||
function hashHas(key) {
|
||||
var data = this.__data__;
|
||||
return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the hash `key` to `value`.
|
||||
*
|
||||
* @private
|
||||
* @name set
|
||||
* @memberOf Hash
|
||||
* @param {string} key The key of the value to set.
|
||||
* @param {*} value The value to set.
|
||||
* @returns {Object} Returns the hash instance.
|
||||
*/
|
||||
function hashSet(key, value) {
|
||||
var data = this.__data__;
|
||||
data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
|
||||
return this;
|
||||
}
|
||||
|
||||
// Add methods to `Hash`.
|
||||
Hash.prototype.clear = hashClear;
|
||||
Hash.prototype['delete'] = hashDelete;
|
||||
Hash.prototype.get = hashGet;
|
||||
Hash.prototype.has = hashHas;
|
||||
Hash.prototype.set = hashSet;
|
||||
|
||||
/**
|
||||
* Creates an list cache object.
|
||||
*
|
||||
* @private
|
||||
* @constructor
|
||||
* @param {Array} [entries] The key-value pairs to cache.
|
||||
*/
|
||||
function ListCache(entries) {
|
||||
var index = -1,
|
||||
length = entries ? entries.length : 0;
|
||||
|
||||
this.clear();
|
||||
while (++index < length) {
|
||||
var entry = entries[index];
|
||||
this.set(entry[0], entry[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all key-value entries from the list cache.
|
||||
*
|
||||
* @private
|
||||
* @name clear
|
||||
* @memberOf ListCache
|
||||
*/
|
||||
function listCacheClear() {
|
||||
this.__data__ = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes `key` and its value from the list cache.
|
||||
*
|
||||
* @private
|
||||
* @name delete
|
||||
* @memberOf ListCache
|
||||
* @param {string} key The key of the value to remove.
|
||||
* @returns {boolean} Returns `true` if the entry was removed, else `false`.
|
||||
*/
|
||||
function listCacheDelete(key) {
|
||||
var data = this.__data__,
|
||||
index = assocIndexOf(data, key);
|
||||
|
||||
if (index < 0) {
|
||||
return false;
|
||||
}
|
||||
var lastIndex = data.length - 1;
|
||||
if (index == lastIndex) {
|
||||
data.pop();
|
||||
} else {
|
||||
splice.call(data, index, 1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list cache value for `key`.
|
||||
*
|
||||
* @private
|
||||
* @name get
|
||||
* @memberOf ListCache
|
||||
* @param {string} key The key of the value to get.
|
||||
* @returns {*} Returns the entry value.
|
||||
*/
|
||||
function listCacheGet(key) {
|
||||
var data = this.__data__,
|
||||
index = assocIndexOf(data, key);
|
||||
|
||||
return index < 0 ? undefined : data[index][1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a list cache value for `key` exists.
|
||||
*
|
||||
* @private
|
||||
* @name has
|
||||
* @memberOf ListCache
|
||||
* @param {string} key The key of the entry to check.
|
||||
* @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
|
||||
*/
|
||||
function listCacheHas(key) {
|
||||
return assocIndexOf(this.__data__, key) > -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the list cache `key` to `value`.
|
||||
*
|
||||
* @private
|
||||
* @name set
|
||||
* @memberOf ListCache
|
||||
* @param {string} key The key of the value to set.
|
||||
* @param {*} value The value to set.
|
||||
* @returns {Object} Returns the list cache instance.
|
||||
*/
|
||||
function listCacheSet(key, value) {
|
||||
var data = this.__data__,
|
||||
index = assocIndexOf(data, key);
|
||||
|
||||
if (index < 0) {
|
||||
data.push([key, value]);
|
||||
} else {
|
||||
data[index][1] = value;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
// Add methods to `ListCache`.
|
||||
ListCache.prototype.clear = listCacheClear;
|
||||
ListCache.prototype['delete'] = listCacheDelete;
|
||||
ListCache.prototype.get = listCacheGet;
|
||||
ListCache.prototype.has = listCacheHas;
|
||||
ListCache.prototype.set = listCacheSet;
|
||||
|
||||
/**
|
||||
* Creates a map cache object to store key-value pairs.
|
||||
*
|
||||
* @private
|
||||
* @constructor
|
||||
* @param {Array} [entries] The key-value pairs to cache.
|
||||
*/
|
||||
function MapCache(entries) {
|
||||
var index = -1,
|
||||
length = entries ? entries.length : 0;
|
||||
|
||||
this.clear();
|
||||
while (++index < length) {
|
||||
var entry = entries[index];
|
||||
this.set(entry[0], entry[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all key-value entries from the map.
|
||||
*
|
||||
* @private
|
||||
* @name clear
|
||||
* @memberOf MapCache
|
||||
*/
|
||||
function mapCacheClear() {
|
||||
this.__data__ = {
|
||||
'hash': new Hash,
|
||||
'map': new (Map || ListCache),
|
||||
'string': new Hash
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes `key` and its value from the map.
|
||||
*
|
||||
* @private
|
||||
* @name delete
|
||||
* @memberOf MapCache
|
||||
* @param {string} key The key of the value to remove.
|
||||
* @returns {boolean} Returns `true` if the entry was removed, else `false`.
|
||||
*/
|
||||
function mapCacheDelete(key) {
|
||||
return getMapData(this, key)['delete'](key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the map value for `key`.
|
||||
*
|
||||
* @private
|
||||
* @name get
|
||||
* @memberOf MapCache
|
||||
* @param {string} key The key of the value to get.
|
||||
* @returns {*} Returns the entry value.
|
||||
*/
|
||||
function mapCacheGet(key) {
|
||||
return getMapData(this, key).get(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a map value for `key` exists.
|
||||
*
|
||||
* @private
|
||||
* @name has
|
||||
* @memberOf MapCache
|
||||
* @param {string} key The key of the entry to check.
|
||||
* @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
|
||||
*/
|
||||
function mapCacheHas(key) {
|
||||
return getMapData(this, key).has(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the map `key` to `value`.
|
||||
*
|
||||
* @private
|
||||
* @name set
|
||||
* @memberOf MapCache
|
||||
* @param {string} key The key of the value to set.
|
||||
* @param {*} value The value to set.
|
||||
* @returns {Object} Returns the map cache instance.
|
||||
*/
|
||||
function mapCacheSet(key, value) {
|
||||
getMapData(this, key).set(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
// Add methods to `MapCache`.
|
||||
MapCache.prototype.clear = mapCacheClear;
|
||||
MapCache.prototype['delete'] = mapCacheDelete;
|
||||
MapCache.prototype.get = mapCacheGet;
|
||||
MapCache.prototype.has = mapCacheHas;
|
||||
MapCache.prototype.set = mapCacheSet;
|
||||
|
||||
/**
|
||||
* Gets the index at which the `key` is found in `array` of key-value pairs.
|
||||
*
|
||||
* @private
|
||||
* @param {Array} array The array to inspect.
|
||||
* @param {*} key The key to search for.
|
||||
* @returns {number} Returns the index of the matched value, else `-1`.
|
||||
*/
|
||||
function assocIndexOf(array, key) {
|
||||
var length = array.length;
|
||||
while (length--) {
|
||||
if (eq(array[length][0], key)) {
|
||||
return length;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* The base implementation of `_.isNative` without bad shim checks.
|
||||
*
|
||||
* @private
|
||||
* @param {*} value The value to check.
|
||||
* @returns {boolean} Returns `true` if `value` is a native function,
|
||||
* else `false`.
|
||||
*/
|
||||
function baseIsNative(value) {
|
||||
if (!isObject(value) || isMasked(value)) {
|
||||
return false;
|
||||
}
|
||||
var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor;
|
||||
return pattern.test(toSource(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data for `map`.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} map The map to query.
|
||||
* @param {string} key The reference key.
|
||||
* @returns {*} Returns the map data.
|
||||
*/
|
||||
function getMapData(map, key) {
|
||||
var data = map.__data__;
|
||||
return isKeyable(key)
|
||||
? data[typeof key == 'string' ? 'string' : 'hash']
|
||||
: data.map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the native function at `key` of `object`.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} object The object to query.
|
||||
* @param {string} key The key of the method to get.
|
||||
* @returns {*} Returns the function if it's native, else `undefined`.
|
||||
*/
|
||||
function getNative(object, key) {
|
||||
var value = getValue(object, key);
|
||||
return baseIsNative(value) ? value : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if `value` is suitable for use as unique object key.
|
||||
*
|
||||
* @private
|
||||
* @param {*} value The value to check.
|
||||
* @returns {boolean} Returns `true` if `value` is suitable, else `false`.
|
||||
*/
|
||||
function isKeyable(value) {
|
||||
var type = typeof value;
|
||||
return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
|
||||
? (value !== '__proto__')
|
||||
: (value === null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if `func` has its source masked.
|
||||
*
|
||||
* @private
|
||||
* @param {Function} func The function to check.
|
||||
* @returns {boolean} Returns `true` if `func` is masked, else `false`.
|
||||
*/
|
||||
function isMasked(func) {
|
||||
return !!maskSrcKey && (maskSrcKey in func);
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts `func` to its source code.
|
||||
*
|
||||
* @private
|
||||
* @param {Function} func The function to process.
|
||||
* @returns {string} Returns the source code.
|
||||
*/
|
||||
function toSource(func) {
|
||||
if (func != null) {
|
||||
try {
|
||||
return funcToString.call(func);
|
||||
} catch (e) {}
|
||||
try {
|
||||
return (func + '');
|
||||
} catch (e) {}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a function that memoizes the result of `func`. If `resolver` is
|
||||
* provided, it determines the cache key for storing the result based on the
|
||||
* arguments provided to the memoized function. By default, the first argument
|
||||
* provided to the memoized function is used as the map cache key. The `func`
|
||||
* is invoked with the `this` binding of the memoized function.
|
||||
*
|
||||
* **Note:** The cache is exposed as the `cache` property on the memoized
|
||||
* function. Its creation may be customized by replacing the `_.memoize.Cache`
|
||||
* constructor with one whose instances implement the
|
||||
* [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)
|
||||
* method interface of `delete`, `get`, `has`, and `set`.
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @since 0.1.0
|
||||
* @category Function
|
||||
* @param {Function} func The function to have its output memoized.
|
||||
* @param {Function} [resolver] The function to resolve the cache key.
|
||||
* @returns {Function} Returns the new memoized function.
|
||||
* @example
|
||||
*
|
||||
* var object = { 'a': 1, 'b': 2 };
|
||||
* var other = { 'c': 3, 'd': 4 };
|
||||
*
|
||||
* var values = _.memoize(_.values);
|
||||
* values(object);
|
||||
* // => [1, 2]
|
||||
*
|
||||
* values(other);
|
||||
* // => [3, 4]
|
||||
*
|
||||
* object.a = 2;
|
||||
* values(object);
|
||||
* // => [1, 2]
|
||||
*
|
||||
* // Modify the result cache.
|
||||
* values.cache.set(object, ['a', 'b']);
|
||||
* values(object);
|
||||
* // => ['a', 'b']
|
||||
*
|
||||
* // Replace `_.memoize.Cache`.
|
||||
* _.memoize.Cache = WeakMap;
|
||||
*/
|
||||
function memoize(func, resolver) {
|
||||
if (typeof func != 'function' || (resolver && typeof resolver != 'function')) {
|
||||
throw new TypeError(FUNC_ERROR_TEXT);
|
||||
}
|
||||
var memoized = function() {
|
||||
var args = arguments,
|
||||
key = resolver ? resolver.apply(this, args) : args[0],
|
||||
cache = memoized.cache;
|
||||
|
||||
if (cache.has(key)) {
|
||||
return cache.get(key);
|
||||
}
|
||||
var result = func.apply(this, args);
|
||||
memoized.cache = cache.set(key, result);
|
||||
return result;
|
||||
};
|
||||
memoized.cache = new (memoize.Cache || MapCache);
|
||||
return memoized;
|
||||
}
|
||||
|
||||
// Assign cache to `_.memoize`.
|
||||
memoize.Cache = MapCache;
|
||||
|
||||
/**
|
||||
* Performs a
|
||||
* [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
|
||||
* comparison between two values to determine if they are equivalent.
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @since 4.0.0
|
||||
* @category Lang
|
||||
* @param {*} value The value to compare.
|
||||
* @param {*} other The other value to compare.
|
||||
* @returns {boolean} Returns `true` if the values are equivalent, else `false`.
|
||||
* @example
|
||||
*
|
||||
* var object = { 'a': 1 };
|
||||
* var other = { 'a': 1 };
|
||||
*
|
||||
* _.eq(object, object);
|
||||
* // => true
|
||||
*
|
||||
* _.eq(object, other);
|
||||
* // => false
|
||||
*
|
||||
* _.eq('a', 'a');
|
||||
* // => true
|
||||
*
|
||||
* _.eq('a', Object('a'));
|
||||
* // => false
|
||||
*
|
||||
* _.eq(NaN, NaN);
|
||||
* // => true
|
||||
*/
|
||||
function eq(value, other) {
|
||||
return value === other || (value !== value && other !== other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if `value` is classified as a `Function` object.
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @since 0.1.0
|
||||
* @category Lang
|
||||
* @param {*} value The value to check.
|
||||
* @returns {boolean} Returns `true` if `value` is a function, else `false`.
|
||||
* @example
|
||||
*
|
||||
* _.isFunction(_);
|
||||
* // => true
|
||||
*
|
||||
* _.isFunction(/abc/);
|
||||
* // => false
|
||||
*/
|
||||
function isFunction(value) {
|
||||
// The use of `Object#toString` avoids issues with the `typeof` operator
|
||||
// in Safari 8-9 which returns 'object' for typed array and other constructors.
|
||||
var tag = isObject(value) ? objectToString.call(value) : '';
|
||||
return tag == funcTag || tag == genTag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if `value` is the
|
||||
* [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
|
||||
* of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
|
||||
*
|
||||
* @static
|
||||
* @memberOf _
|
||||
* @since 0.1.0
|
||||
* @category Lang
|
||||
* @param {*} value The value to check.
|
||||
* @returns {boolean} Returns `true` if `value` is an object, else `false`.
|
||||
* @example
|
||||
*
|
||||
* _.isObject({});
|
||||
* // => true
|
||||
*
|
||||
* _.isObject([1, 2, 3]);
|
||||
* // => true
|
||||
*
|
||||
* _.isObject(_.noop);
|
||||
* // => true
|
||||
*
|
||||
* _.isObject(null);
|
||||
* // => false
|
||||
*/
|
||||
function isObject(value) {
|
||||
var type = typeof value;
|
||||
return !!value && (type == 'object' || type == 'function');
|
||||
}
|
||||
|
||||
module.exports = memoize;
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "lodash.memoize",
|
||||
"version": "4.1.2",
|
||||
"description": "The lodash method `_.memoize` exported as a module.",
|
||||
"homepage": "https://lodash.com/",
|
||||
"icon": "https://lodash.com/icon.svg",
|
||||
"license": "MIT",
|
||||
"keywords": "lodash-modularized, memoize",
|
||||
"author": "John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||
"contributors": [
|
||||
"John-David Dalton <john.david.dalton@gmail.com> (http://allyoucanleet.com/)",
|
||||
"Blaine Bublitz <blaine.bublitz@gmail.com> (https://github.com/phated)",
|
||||
"Mathias Bynens <mathias@qiwi.be> (https://mathiasbynens.be/)"
|
||||
],
|
||||
"repository": "lodash/lodash",
|
||||
"scripts": { "test": "echo \"See https://travis-ci.org/lodash/lodash-cli for testing details.\"" }
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
# Changers Lorgs!
|
||||
|
||||
## 1.0
|
||||
|
||||
Full rewrite. Essentially a brand new module.
|
||||
|
||||
- Return a promise instead of taking a callback.
|
||||
- Use native `fs.mkdir(path, { recursive: true })` when available.
|
||||
- Drop support for outdated Node.js versions. (Technically still works on
|
||||
Node.js v8, but only 10 and above are officially supported.)
|
||||
|
||||
## 0.x
|
||||
|
||||
Original and most widely used recursive directory creation implementation
|
||||
in JavaScript, dating back to 2010.
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
Copyright 2010 James Halliday (mail@substack.net)
|
||||
Copyright James Halliday (mail@substack.net) and Isaac Z. Schlueter (i@izs.me)
|
||||
|
||||
This project is free software released under the MIT/X11 license:
|
||||
This project is free software released under the MIT license:
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+62
-27
@@ -1,33 +1,68 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var mkdirp = require('../');
|
||||
var minimist = require('minimist');
|
||||
var fs = require('fs');
|
||||
const usage = () => `
|
||||
usage: mkdirp [DIR1,DIR2..] {OPTIONS}
|
||||
|
||||
var argv = minimist(process.argv.slice(2), {
|
||||
alias: { m: 'mode', h: 'help' },
|
||||
string: [ 'mode' ]
|
||||
});
|
||||
if (argv.help) {
|
||||
fs.createReadStream(__dirname + '/usage.txt').pipe(process.stdout);
|
||||
return;
|
||||
Create each supplied directory including any necessary parent directories
|
||||
that don't yet exist.
|
||||
|
||||
If the directory already exists, do nothing.
|
||||
|
||||
OPTIONS are:
|
||||
|
||||
-m<mode> If a directory needs to be created, set the mode as an octal
|
||||
--mode=<mode> permission string.
|
||||
|
||||
-v --version Print the mkdirp version number
|
||||
|
||||
-h --help Print this helpful banner
|
||||
|
||||
-p --print Print the first directories created for each path provided
|
||||
|
||||
--manual Use manual implementation, even if native is available
|
||||
`
|
||||
|
||||
const dirs = []
|
||||
const opts = {}
|
||||
let print = false
|
||||
let dashdash = false
|
||||
let manual = false
|
||||
for (const arg of process.argv.slice(2)) {
|
||||
if (dashdash)
|
||||
dirs.push(arg)
|
||||
else if (arg === '--')
|
||||
dashdash = true
|
||||
else if (arg === '--manual')
|
||||
manual = true
|
||||
else if (/^-h/.test(arg) || /^--help/.test(arg)) {
|
||||
console.log(usage())
|
||||
process.exit(0)
|
||||
} else if (arg === '-v' || arg === '--version') {
|
||||
console.log(require('../package.json').version)
|
||||
process.exit(0)
|
||||
} else if (arg === '-p' || arg === '--print') {
|
||||
print = true
|
||||
} else if (/^-m/.test(arg) || /^--mode=/.test(arg)) {
|
||||
const mode = parseInt(arg.replace(/^(-m|--mode=)/, ''), 8)
|
||||
if (isNaN(mode)) {
|
||||
console.error(`invalid mode argument: ${arg}\nMust be an octal number.`)
|
||||
process.exit(1)
|
||||
}
|
||||
opts.mode = mode
|
||||
} else
|
||||
dirs.push(arg)
|
||||
}
|
||||
|
||||
var paths = argv._.slice();
|
||||
var mode = argv.mode ? parseInt(argv.mode, 8) : undefined;
|
||||
const mkdirp = require('../')
|
||||
const impl = manual ? mkdirp.manual : mkdirp
|
||||
if (dirs.length === 0)
|
||||
console.error(usage())
|
||||
|
||||
(function next () {
|
||||
if (paths.length === 0) return;
|
||||
var p = paths.shift();
|
||||
|
||||
if (mode === undefined) mkdirp(p, cb)
|
||||
else mkdirp(p, mode, cb)
|
||||
|
||||
function cb (err) {
|
||||
if (err) {
|
||||
console.error(err.message);
|
||||
process.exit(1);
|
||||
}
|
||||
else next();
|
||||
}
|
||||
})();
|
||||
Promise.all(dirs.map(dir => impl(dir, opts)))
|
||||
.then(made => print ? made.forEach(m => m && console.log(m)) : null)
|
||||
.catch(er => {
|
||||
console.error(er.message)
|
||||
if (er.code)
|
||||
console.error(' code: ' + er.code)
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
usage: mkdirp [DIR1,DIR2..] {OPTIONS}
|
||||
|
||||
Create each supplied directory including any necessary parent directories that
|
||||
don't yet exist.
|
||||
|
||||
If the directory already exists, do nothing.
|
||||
|
||||
OPTIONS are:
|
||||
|
||||
-m, --mode If a directory needs to be created, set the mode as an octal
|
||||
permission string.
|
||||
|
||||
+24
-92
@@ -1,99 +1,31 @@
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
var _0777 = parseInt('0777', 8);
|
||||
const optsArg = require('./lib/opts-arg.js')
|
||||
const pathArg = require('./lib/path-arg.js')
|
||||
|
||||
module.exports = mkdirP.mkdirp = mkdirP.mkdirP = mkdirP;
|
||||
const {mkdirpNative, mkdirpNativeSync} = require('./lib/mkdirp-native.js')
|
||||
const {mkdirpManual, mkdirpManualSync} = require('./lib/mkdirp-manual.js')
|
||||
const {useNative, useNativeSync} = require('./lib/use-native.js')
|
||||
|
||||
function mkdirP (p, opts, f, made) {
|
||||
if (typeof opts === 'function') {
|
||||
f = opts;
|
||||
opts = {};
|
||||
}
|
||||
else if (!opts || typeof opts !== 'object') {
|
||||
opts = { mode: opts };
|
||||
}
|
||||
|
||||
var mode = opts.mode;
|
||||
var xfs = opts.fs || fs;
|
||||
|
||||
if (mode === undefined) {
|
||||
mode = _0777
|
||||
}
|
||||
if (!made) made = null;
|
||||
|
||||
var cb = f || function () {};
|
||||
p = path.resolve(p);
|
||||
|
||||
xfs.mkdir(p, mode, function (er) {
|
||||
if (!er) {
|
||||
made = made || p;
|
||||
return cb(null, made);
|
||||
}
|
||||
switch (er.code) {
|
||||
case 'ENOENT':
|
||||
if (path.dirname(p) === p) return cb(er);
|
||||
mkdirP(path.dirname(p), opts, function (er, made) {
|
||||
if (er) cb(er, made);
|
||||
else mkdirP(p, opts, cb, made);
|
||||
});
|
||||
break;
|
||||
|
||||
// In the case of any other error, just see if there's a dir
|
||||
// there already. If so, then hooray! If not, then something
|
||||
// is borked.
|
||||
default:
|
||||
xfs.stat(p, function (er2, stat) {
|
||||
// if the stat fails, then that's super weird.
|
||||
// let the original error be the failure reason.
|
||||
if (er2 || !stat.isDirectory()) cb(er, made)
|
||||
else cb(null, made);
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
const mkdirp = (path, opts) => {
|
||||
path = pathArg(path)
|
||||
opts = optsArg(opts)
|
||||
return useNative(opts)
|
||||
? mkdirpNative(path, opts)
|
||||
: mkdirpManual(path, opts)
|
||||
}
|
||||
|
||||
mkdirP.sync = function sync (p, opts, made) {
|
||||
if (!opts || typeof opts !== 'object') {
|
||||
opts = { mode: opts };
|
||||
}
|
||||
|
||||
var mode = opts.mode;
|
||||
var xfs = opts.fs || fs;
|
||||
|
||||
if (mode === undefined) {
|
||||
mode = _0777
|
||||
}
|
||||
if (!made) made = null;
|
||||
const mkdirpSync = (path, opts) => {
|
||||
path = pathArg(path)
|
||||
opts = optsArg(opts)
|
||||
return useNativeSync(opts)
|
||||
? mkdirpNativeSync(path, opts)
|
||||
: mkdirpManualSync(path, opts)
|
||||
}
|
||||
|
||||
p = path.resolve(p);
|
||||
mkdirp.sync = mkdirpSync
|
||||
mkdirp.native = (path, opts) => mkdirpNative(pathArg(path), optsArg(opts))
|
||||
mkdirp.manual = (path, opts) => mkdirpManual(pathArg(path), optsArg(opts))
|
||||
mkdirp.nativeSync = (path, opts) => mkdirpNativeSync(pathArg(path), optsArg(opts))
|
||||
mkdirp.manualSync = (path, opts) => mkdirpManualSync(pathArg(path), optsArg(opts))
|
||||
|
||||
try {
|
||||
xfs.mkdirSync(p, mode);
|
||||
made = made || p;
|
||||
}
|
||||
catch (err0) {
|
||||
switch (err0.code) {
|
||||
case 'ENOENT' :
|
||||
made = sync(path.dirname(p), opts, made);
|
||||
sync(p, opts, made);
|
||||
break;
|
||||
|
||||
// In the case of any other error, just see if there's a dir
|
||||
// there already. If so, then hooray! If not, then something
|
||||
// is borked.
|
||||
default:
|
||||
var stat;
|
||||
try {
|
||||
stat = xfs.statSync(p);
|
||||
}
|
||||
catch (err1) {
|
||||
throw err0;
|
||||
}
|
||||
if (!stat.isDirectory()) throw err0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return made;
|
||||
};
|
||||
module.exports = mkdirp
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
const {dirname} = require('path')
|
||||
|
||||
const findMade = (opts, parent, path = undefined) => {
|
||||
// we never want the 'made' return value to be a root directory
|
||||
if (path === parent)
|
||||
return Promise.resolve()
|
||||
|
||||
return opts.statAsync(parent).then(
|
||||
st => st.isDirectory() ? path : undefined, // will fail later
|
||||
er => er.code === 'ENOENT'
|
||||
? findMade(opts, dirname(parent), parent)
|
||||
: undefined
|
||||
)
|
||||
}
|
||||
|
||||
const findMadeSync = (opts, parent, path = undefined) => {
|
||||
if (path === parent)
|
||||
return undefined
|
||||
|
||||
try {
|
||||
return opts.statSync(parent).isDirectory() ? path : undefined
|
||||
} catch (er) {
|
||||
return er.code === 'ENOENT'
|
||||
? findMadeSync(opts, dirname(parent), parent)
|
||||
: undefined
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {findMade, findMadeSync}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
const {dirname} = require('path')
|
||||
|
||||
const mkdirpManual = (path, opts, made) => {
|
||||
opts.recursive = false
|
||||
const parent = dirname(path)
|
||||
if (parent === path) {
|
||||
return opts.mkdirAsync(path, opts).catch(er => {
|
||||
// swallowed by recursive implementation on posix systems
|
||||
// any other error is a failure
|
||||
if (er.code !== 'EISDIR')
|
||||
throw er
|
||||
})
|
||||
}
|
||||
|
||||
return opts.mkdirAsync(path, opts).then(() => made || path, er => {
|
||||
if (er.code === 'ENOENT')
|
||||
return mkdirpManual(parent, opts)
|
||||
.then(made => mkdirpManual(path, opts, made))
|
||||
if (er.code !== 'EEXIST' && er.code !== 'EROFS')
|
||||
throw er
|
||||
return opts.statAsync(path).then(st => {
|
||||
if (st.isDirectory())
|
||||
return made
|
||||
else
|
||||
throw er
|
||||
}, () => { throw er })
|
||||
})
|
||||
}
|
||||
|
||||
const mkdirpManualSync = (path, opts, made) => {
|
||||
const parent = dirname(path)
|
||||
opts.recursive = false
|
||||
|
||||
if (parent === path) {
|
||||
try {
|
||||
return opts.mkdirSync(path, opts)
|
||||
} catch (er) {
|
||||
// swallowed by recursive implementation on posix systems
|
||||
// any other error is a failure
|
||||
if (er.code !== 'EISDIR')
|
||||
throw er
|
||||
else
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
opts.mkdirSync(path, opts)
|
||||
return made || path
|
||||
} catch (er) {
|
||||
if (er.code === 'ENOENT')
|
||||
return mkdirpManualSync(path, opts, mkdirpManualSync(parent, opts, made))
|
||||
if (er.code !== 'EEXIST' && er.code !== 'EROFS')
|
||||
throw er
|
||||
try {
|
||||
if (!opts.statSync(path).isDirectory())
|
||||
throw er
|
||||
} catch (_) {
|
||||
throw er
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {mkdirpManual, mkdirpManualSync}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
const {dirname} = require('path')
|
||||
const {findMade, findMadeSync} = require('./find-made.js')
|
||||
const {mkdirpManual, mkdirpManualSync} = require('./mkdirp-manual.js')
|
||||
|
||||
const mkdirpNative = (path, opts) => {
|
||||
opts.recursive = true
|
||||
const parent = dirname(path)
|
||||
if (parent === path)
|
||||
return opts.mkdirAsync(path, opts)
|
||||
|
||||
return findMade(opts, path).then(made =>
|
||||
opts.mkdirAsync(path, opts).then(() => made)
|
||||
.catch(er => {
|
||||
if (er.code === 'ENOENT')
|
||||
return mkdirpManual(path, opts)
|
||||
else
|
||||
throw er
|
||||
}))
|
||||
}
|
||||
|
||||
const mkdirpNativeSync = (path, opts) => {
|
||||
opts.recursive = true
|
||||
const parent = dirname(path)
|
||||
if (parent === path)
|
||||
return opts.mkdirSync(path, opts)
|
||||
|
||||
const made = findMadeSync(opts, path)
|
||||
try {
|
||||
opts.mkdirSync(path, opts)
|
||||
return made
|
||||
} catch (er) {
|
||||
if (er.code === 'ENOENT')
|
||||
return mkdirpManualSync(path, opts)
|
||||
else
|
||||
throw er
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {mkdirpNative, mkdirpNativeSync}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
const { promisify } = require('util')
|
||||
const fs = require('fs')
|
||||
const optsArg = opts => {
|
||||
if (!opts)
|
||||
opts = { mode: 0o777, fs }
|
||||
else if (typeof opts === 'object')
|
||||
opts = { mode: 0o777, fs, ...opts }
|
||||
else if (typeof opts === 'number')
|
||||
opts = { mode: opts, fs }
|
||||
else if (typeof opts === 'string')
|
||||
opts = { mode: parseInt(opts, 8), fs }
|
||||
else
|
||||
throw new TypeError('invalid options argument')
|
||||
|
||||
opts.mkdir = opts.mkdir || opts.fs.mkdir || fs.mkdir
|
||||
opts.mkdirAsync = promisify(opts.mkdir)
|
||||
opts.stat = opts.stat || opts.fs.stat || fs.stat
|
||||
opts.statAsync = promisify(opts.stat)
|
||||
opts.statSync = opts.statSync || opts.fs.statSync || fs.statSync
|
||||
opts.mkdirSync = opts.mkdirSync || opts.fs.mkdirSync || fs.mkdirSync
|
||||
return opts
|
||||
}
|
||||
module.exports = optsArg
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
const platform = process.env.__TESTING_MKDIRP_PLATFORM__ || process.platform
|
||||
const { resolve, parse } = require('path')
|
||||
const pathArg = path => {
|
||||
if (/\0/.test(path)) {
|
||||
// simulate same failure that node raises
|
||||
throw Object.assign(
|
||||
new TypeError('path must be a string without null bytes'),
|
||||
{
|
||||
path,
|
||||
code: 'ERR_INVALID_ARG_VALUE',
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
path = resolve(path)
|
||||
if (platform === 'win32') {
|
||||
const badWinChars = /[*|"<>?:]/
|
||||
const {root} = parse(path)
|
||||
if (badWinChars.test(path.substr(root.length))) {
|
||||
throw Object.assign(new Error('Illegal characters in path.'), {
|
||||
path,
|
||||
code: 'EINVAL',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
module.exports = pathArg
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
const fs = require('fs')
|
||||
|
||||
const version = process.env.__TESTING_MKDIRP_NODE_VERSION__ || process.version
|
||||
const versArr = version.replace(/^v/, '').split('.')
|
||||
const hasNative = +versArr[0] > 10 || +versArr[0] === 10 && +versArr[1] >= 12
|
||||
|
||||
const useNative = !hasNative ? () => false : opts => opts.mkdir === fs.mkdir
|
||||
const useNativeSync = !hasNative ? () => false : opts => opts.mkdirSync === fs.mkdirSync
|
||||
|
||||
module.exports = {useNative, useNativeSync}
|
||||
+22
-12
@@ -1,34 +1,44 @@
|
||||
{
|
||||
"name": "mkdirp",
|
||||
"description": "Recursively mkdir, like `mkdir -p`",
|
||||
"version": "0.5.5",
|
||||
"publishConfig": {
|
||||
"tag": "legacy"
|
||||
},
|
||||
"author": "James Halliday <mail@substack.net> (http://substack.net)",
|
||||
"version": "1.0.4",
|
||||
"main": "index.js",
|
||||
"keywords": [
|
||||
"mkdir",
|
||||
"directory"
|
||||
"directory",
|
||||
"make dir",
|
||||
"make",
|
||||
"dir",
|
||||
"recursive",
|
||||
"native"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/substack/node-mkdirp.git"
|
||||
"url": "https://github.com/isaacs/node-mkdirp.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "tap test/*.js"
|
||||
"test": "tap",
|
||||
"snap": "tap",
|
||||
"preversion": "npm test",
|
||||
"postversion": "npm publish",
|
||||
"postpublish": "git push origin --follow-tags"
|
||||
},
|
||||
"dependencies": {
|
||||
"minimist": "^1.2.5"
|
||||
"tap": {
|
||||
"check-coverage": true,
|
||||
"coverage-map": "map.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mock-fs": "^3.7.0",
|
||||
"tap": "^5.4.2"
|
||||
"require-inject": "^1.4.4",
|
||||
"tap": "^14.10.7"
|
||||
},
|
||||
"bin": "bin/cmd.js",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"files": [
|
||||
"bin",
|
||||
"lib",
|
||||
"index.js"
|
||||
]
|
||||
}
|
||||
|
||||
+206
-40
@@ -1,26 +1,37 @@
|
||||
# mkdirp
|
||||
|
||||
Like `mkdir -p`, but in node.js!
|
||||
Like `mkdir -p`, but in Node.js!
|
||||
|
||||
[](http://travis-ci.org/substack/node-mkdirp)
|
||||
Now with a modern API and no\* bugs!
|
||||
|
||||
<small>\* may contain some bugs</small>
|
||||
|
||||
# example
|
||||
|
||||
## pow.js
|
||||
|
||||
```js
|
||||
var mkdirp = require('mkdirp');
|
||||
|
||||
mkdirp('/tmp/foo/bar/baz', function (err) {
|
||||
if (err) console.error(err)
|
||||
else console.log('pow!')
|
||||
});
|
||||
const mkdirp = require('mkdirp')
|
||||
|
||||
// return value is a Promise resolving to the first directory created
|
||||
mkdirp('/tmp/foo/bar/baz').then(made =>
|
||||
console.log(`made directories, starting with ${made}`))
|
||||
```
|
||||
|
||||
Output
|
||||
Output (where `/tmp/foo` already exists)
|
||||
|
||||
```
|
||||
pow!
|
||||
made directories, starting with /tmp/foo/bar
|
||||
```
|
||||
|
||||
Or, if you don't have time to wait around for promises:
|
||||
|
||||
```js
|
||||
const mkdirp = require('mkdirp')
|
||||
|
||||
// return value is the first directory created
|
||||
const made = mkdirp.sync('/tmp/foo/bar/baz')
|
||||
console.log(`made directories, starting with ${made}`)
|
||||
```
|
||||
|
||||
And now /tmp/foo/bar/baz exists, huzzah!
|
||||
@@ -28,55 +39,198 @@ And now /tmp/foo/bar/baz exists, huzzah!
|
||||
# methods
|
||||
|
||||
```js
|
||||
var mkdirp = require('mkdirp');
|
||||
const mkdirp = require('mkdirp')
|
||||
```
|
||||
|
||||
## mkdirp(dir, opts, cb)
|
||||
## mkdirp(dir, [opts]) -> Promise<String | undefined>
|
||||
|
||||
Create a new directory and any necessary subdirectories at `dir` with octal
|
||||
permission string `opts.mode`. If `opts` is a non-object, it will be treated as
|
||||
the `opts.mode`.
|
||||
|
||||
If `opts.mode` isn't specified, it defaults to `0777`.
|
||||
|
||||
`cb(err, made)` fires with the error or the first directory `made`
|
||||
that had to be created, if any.
|
||||
|
||||
You can optionally pass in an alternate `fs` implementation by passing in
|
||||
`opts.fs`. Your implementation should have `opts.fs.mkdir(path, mode, cb)` and
|
||||
`opts.fs.stat(path, cb)`.
|
||||
|
||||
## mkdirp.sync(dir, opts)
|
||||
|
||||
Synchronously create a new directory and any necessary subdirectories at `dir`
|
||||
with octal permission string `opts.mode`. If `opts` is a non-object, it will be
|
||||
permission string `opts.mode`. If `opts` is a string or number, it will be
|
||||
treated as the `opts.mode`.
|
||||
|
||||
If `opts.mode` isn't specified, it defaults to `0777`.
|
||||
If `opts.mode` isn't specified, it defaults to `0o777 &
|
||||
(~process.umask())`.
|
||||
|
||||
Returns the first directory that had to be created, if any.
|
||||
Promise resolves to first directory `made` that had to be created, or
|
||||
`undefined` if everything already exists. Promise rejects if any errors
|
||||
are encountered. Note that, in the case of promise rejection, some
|
||||
directories _may_ have been created, as recursive directory creation is not
|
||||
an atomic operation.
|
||||
|
||||
You can optionally pass in an alternate `fs` implementation by passing in
|
||||
`opts.fs`. Your implementation should have `opts.fs.mkdirSync(path, mode)` and
|
||||
`opts.fs.statSync(path)`.
|
||||
`opts.fs`. Your implementation should have `opts.fs.mkdir(path, opts, cb)`
|
||||
and `opts.fs.stat(path, cb)`.
|
||||
|
||||
# usage
|
||||
You can also override just one or the other of `mkdir` and `stat` by
|
||||
passing in `opts.stat` or `opts.mkdir`, or providing an `fs` option that
|
||||
only overrides one of these.
|
||||
|
||||
## mkdirp.sync(dir, opts) -> String|null
|
||||
|
||||
Synchronously create a new directory and any necessary subdirectories at
|
||||
`dir` with octal permission string `opts.mode`. If `opts` is a string or
|
||||
number, it will be treated as the `opts.mode`.
|
||||
|
||||
If `opts.mode` isn't specified, it defaults to `0o777 &
|
||||
(~process.umask())`.
|
||||
|
||||
Returns the first directory that had to be created, or undefined if
|
||||
everything already exists.
|
||||
|
||||
You can optionally pass in an alternate `fs` implementation by passing in
|
||||
`opts.fs`. Your implementation should have `opts.fs.mkdirSync(path, mode)`
|
||||
and `opts.fs.statSync(path)`.
|
||||
|
||||
You can also override just one or the other of `mkdirSync` and `statSync`
|
||||
by passing in `opts.statSync` or `opts.mkdirSync`, or providing an `fs`
|
||||
option that only overrides one of these.
|
||||
|
||||
## mkdirp.manual, mkdirp.manualSync
|
||||
|
||||
Use the manual implementation (not the native one). This is the default
|
||||
when the native implementation is not available or the stat/mkdir
|
||||
implementation is overridden.
|
||||
|
||||
## mkdirp.native, mkdirp.nativeSync
|
||||
|
||||
Use the native implementation (not the manual one). This is the default
|
||||
when the native implementation is available and stat/mkdir are not
|
||||
overridden.
|
||||
|
||||
# implementation
|
||||
|
||||
On Node.js v10.12.0 and above, use the native `fs.mkdir(p,
|
||||
{recursive:true})` option, unless `fs.mkdir`/`fs.mkdirSync` has been
|
||||
overridden by an option.
|
||||
|
||||
## native implementation
|
||||
|
||||
- If the path is a root directory, then pass it to the underlying
|
||||
implementation and return the result/error. (In this case, it'll either
|
||||
succeed or fail, but we aren't actually creating any dirs.)
|
||||
- Walk up the path statting each directory, to find the first path that
|
||||
will be created, `made`.
|
||||
- Call `fs.mkdir(path, { recursive: true })` (or `fs.mkdirSync`)
|
||||
- If error, raise it to the caller.
|
||||
- Return `made`.
|
||||
|
||||
## manual implementation
|
||||
|
||||
- Call underlying `fs.mkdir` implementation, with `recursive: false`
|
||||
- If error:
|
||||
- If path is a root directory, raise to the caller and do not handle it
|
||||
- If ENOENT, mkdirp parent dir, store result as `made`
|
||||
- stat(path)
|
||||
- If error, raise original `mkdir` error
|
||||
- If directory, return `made`
|
||||
- Else, raise original `mkdir` error
|
||||
- else
|
||||
- return `undefined` if a root dir, or `made` if set, or `path`
|
||||
|
||||
## windows vs unix caveat
|
||||
|
||||
On Windows file systems, attempts to create a root directory (ie, a drive
|
||||
letter or root UNC path) will fail. If the root directory exists, then it
|
||||
will fail with `EPERM`. If the root directory does not exist, then it will
|
||||
fail with `ENOENT`.
|
||||
|
||||
On posix file systems, attempts to create a root directory (in recursive
|
||||
mode) will succeed silently, as it is treated like just another directory
|
||||
that already exists. (In non-recursive mode, of course, it fails with
|
||||
`EEXIST`.)
|
||||
|
||||
In order to preserve this system-specific behavior (and because it's not as
|
||||
if we can create the parent of a root directory anyway), attempts to create
|
||||
a root directory are passed directly to the `fs` implementation, and any
|
||||
errors encountered are not handled.
|
||||
|
||||
## native error caveat
|
||||
|
||||
The native implementation (as of at least Node.js v13.4.0) does not provide
|
||||
appropriate errors in some cases (see
|
||||
[nodejs/node#31481](https://github.com/nodejs/node/issues/31481) and
|
||||
[nodejs/node#28015](https://github.com/nodejs/node/issues/28015)).
|
||||
|
||||
In order to work around this issue, the native implementation will fall
|
||||
back to the manual implementation if an `ENOENT` error is encountered.
|
||||
|
||||
# choosing a recursive mkdir implementation
|
||||
|
||||
There are a few to choose from! Use the one that suits your needs best :D
|
||||
|
||||
## use `fs.mkdir(path, {recursive: true}, cb)` if:
|
||||
|
||||
- You wish to optimize performance even at the expense of other factors.
|
||||
- You don't need to know the first dir created.
|
||||
- You are ok with getting `ENOENT` as the error when some other problem is
|
||||
the actual cause.
|
||||
- You can limit your platforms to Node.js v10.12 and above.
|
||||
- You're ok with using callbacks instead of promises.
|
||||
- You don't need/want a CLI.
|
||||
- You don't need to override the `fs` methods in use.
|
||||
|
||||
## use this module (mkdirp 1.x) if:
|
||||
|
||||
- You need to know the first directory that was created.
|
||||
- You wish to use the native implementation if available, but fall back
|
||||
when it's not.
|
||||
- You prefer promise-returning APIs to callback-taking APIs.
|
||||
- You want more useful error messages than the native recursive mkdir
|
||||
provides (at least as of Node.js v13.4), and are ok with re-trying on
|
||||
`ENOENT` to achieve this.
|
||||
- You need (or at least, are ok with) a CLI.
|
||||
- You need to override the `fs` methods in use.
|
||||
|
||||
## use [`make-dir`](http://npm.im/make-dir) if:
|
||||
|
||||
- You do not need to know the first dir created (and wish to save a few
|
||||
`stat` calls when using the native implementation for this reason).
|
||||
- You wish to use the native implementation if available, but fall back
|
||||
when it's not.
|
||||
- You prefer promise-returning APIs to callback-taking APIs.
|
||||
- You are ok with occasionally getting `ENOENT` errors for failures that
|
||||
are actually related to something other than a missing file system entry.
|
||||
- You don't need/want a CLI.
|
||||
- You need to override the `fs` methods in use.
|
||||
|
||||
## use mkdirp 0.x if:
|
||||
|
||||
- You need to know the first directory that was created.
|
||||
- You need (or at least, are ok with) a CLI.
|
||||
- You need to override the `fs` methods in use.
|
||||
- You're ok with using callbacks instead of promises.
|
||||
- You are not running on Windows, where the root-level ENOENT errors can
|
||||
lead to infinite regress.
|
||||
- You think vinyl just sounds warmer and richer for some weird reason.
|
||||
- You are supporting truly ancient Node.js versions, before even the advent
|
||||
of a `Promise` language primitive. (Please don't. You deserve better.)
|
||||
|
||||
# cli
|
||||
|
||||
This package also ships with a `mkdirp` command.
|
||||
|
||||
```
|
||||
$ mkdirp -h
|
||||
|
||||
usage: mkdirp [DIR1,DIR2..] {OPTIONS}
|
||||
|
||||
Create each supplied directory including any necessary parent directories that
|
||||
don't yet exist.
|
||||
|
||||
Create each supplied directory including any necessary parent directories
|
||||
that don't yet exist.
|
||||
|
||||
If the directory already exists, do nothing.
|
||||
|
||||
OPTIONS are:
|
||||
|
||||
-m, --mode If a directory needs to be created, set the mode as an octal
|
||||
permission string.
|
||||
-m<mode> If a directory needs to be created, set the mode as an octal
|
||||
--mode=<mode> permission string.
|
||||
|
||||
-v --version Print the mkdirp version number
|
||||
|
||||
-h --help Print this helpful banner
|
||||
|
||||
-p --print Print the first directories created for each path provided
|
||||
|
||||
--manual Use manual implementation, even if native is available
|
||||
```
|
||||
|
||||
# install
|
||||
@@ -87,13 +241,25 @@ With [npm](http://npmjs.org) do:
|
||||
npm install mkdirp
|
||||
```
|
||||
|
||||
to get the library, or
|
||||
to get the library locally, or
|
||||
|
||||
```
|
||||
npm install -g mkdirp
|
||||
```
|
||||
|
||||
to get the command.
|
||||
to get the command everywhere, or
|
||||
|
||||
```
|
||||
npx mkdirp ...
|
||||
```
|
||||
|
||||
to run the command without installing it globally.
|
||||
|
||||
# platform support
|
||||
|
||||
This module works on node v8, but only v10 and above are officially
|
||||
supported, as Node v8 reached its LTS end of life 2020-01-01, which is in
|
||||
the past, as of this writing.
|
||||
|
||||
# license
|
||||
|
||||
|
||||
+6001
File diff suppressed because it is too large
Load Diff
+109
@@ -0,0 +1,109 @@
|
||||

|
||||
|
||||
<h2 align="center">Opinionated Code Formatter</h2>
|
||||
|
||||
<p align="center">
|
||||
<em>
|
||||
JavaScript
|
||||
· TypeScript
|
||||
· Flow
|
||||
· JSX
|
||||
· JSON
|
||||
</em>
|
||||
<br />
|
||||
<em>
|
||||
CSS
|
||||
· SCSS
|
||||
· Less
|
||||
</em>
|
||||
<br />
|
||||
<em>
|
||||
HTML
|
||||
· Vue
|
||||
· Angular
|
||||
</em>
|
||||
<br />
|
||||
<em>
|
||||
GraphQL
|
||||
· Markdown
|
||||
· YAML
|
||||
</em>
|
||||
<br />
|
||||
<em>
|
||||
<a href="https://prettier.io/docs/en/plugins.html">
|
||||
Your favorite language?
|
||||
</a>
|
||||
</em>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/prettier/prettier/actions?query=workflow%3AProd+branch%3Amain">
|
||||
<img alt="Github Actions Build Status" src="https://img.shields.io/github/workflow/status/prettier/prettier/Prod?label=Prod&style=flat-square"></a>
|
||||
<a href="https://github.com/prettier/prettier/actions?query=workflow%3ADev+branch%3Amain">
|
||||
<img alt="Github Actions Build Status" src="https://img.shields.io/github/workflow/status/prettier/prettier/Dev?label=Dev&style=flat-square"></a>
|
||||
<a href="https://github.com/prettier/prettier/actions?query=workflow%3ALint+branch%3Amain">
|
||||
<img alt="Github Actions Build Status" src="https://img.shields.io/github/workflow/status/prettier/prettier/Lint?label=Lint&style=flat-square"></a>
|
||||
<a href="https://codecov.io/gh/prettier/prettier">
|
||||
<img alt="Codecov Coverage Status" src="https://img.shields.io/codecov/c/github/prettier/prettier.svg?style=flat-square"></a>
|
||||
<a href="https://twitter.com/acdlite/status/974390255393505280">
|
||||
<img alt="Blazing Fast" src="https://img.shields.io/badge/speed-blazing%20%F0%9F%94%A5-brightgreen.svg?style=flat-square"></a>
|
||||
<br/>
|
||||
<a href="https://www.npmjs.com/package/prettier">
|
||||
<img alt="npm version" src="https://img.shields.io/npm/v/prettier.svg?style=flat-square"></a>
|
||||
<a href="https://www.npmjs.com/package/prettier">
|
||||
<img alt="weekly downloads from npm" src="https://img.shields.io/npm/dw/prettier.svg?style=flat-square"></a>
|
||||
<a href="#badge">
|
||||
<img alt="code style: prettier" src="https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square"></a>
|
||||
<a href="https://twitter.com/PrettierCode">
|
||||
<img alt="Follow Prettier on Twitter" src="https://img.shields.io/twitter/follow/prettiercode.svg?label=follow+prettier&style=flat-square"></a>
|
||||
</p>
|
||||
|
||||
## Intro
|
||||
|
||||
Prettier is an opinionated code formatter. It enforces a consistent style by parsing your code and re-printing it with its own rules that take the maximum line length into account, wrapping code when necessary.
|
||||
|
||||
### Input
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
```js
|
||||
foo(reallyLongArg(), omgSoManyParameters(), IShouldRefactorThis(), isThereSeriouslyAnotherOne());
|
||||
```
|
||||
|
||||
### Output
|
||||
|
||||
```js
|
||||
foo(
|
||||
reallyLongArg(),
|
||||
omgSoManyParameters(),
|
||||
IShouldRefactorThis(),
|
||||
isThereSeriouslyAnotherOne()
|
||||
);
|
||||
```
|
||||
|
||||
Prettier can be run [in your editor](https://prettier.io/docs/en/editors.html) on-save, in a [pre-commit hook](https://prettier.io/docs/en/precommit.html), or in [CI environments](https://prettier.io/docs/en/cli.html#list-different) to ensure your codebase has a consistent style without devs ever having to post a nit-picky comment on a code review ever again!
|
||||
|
||||
---
|
||||
|
||||
**[Documentation](https://prettier.io/docs/en/)**
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
[Install](https://prettier.io/docs/en/install.html) ·
|
||||
[Options](https://prettier.io/docs/en/options.html) ·
|
||||
[CLI](https://prettier.io/docs/en/cli.html) ·
|
||||
[API](https://prettier.io/docs/en/api.html)
|
||||
|
||||
**[Playground](https://prettier.io/playground/)**
|
||||
|
||||
---
|
||||
|
||||
## Badge
|
||||
|
||||
Show the world you're using _Prettier_ → [](https://github.com/prettier/prettier)
|
||||
|
||||
```md
|
||||
[](https://github.com/prettier/prettier)
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||
var __commonJS = function(cb, mod) {
|
||||
return function __require() {
|
||||
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
||||
};
|
||||
};
|
||||
|
||||
// node_modules/semver-compare/index.js
|
||||
var require_semver_compare = __commonJS({
|
||||
"node_modules/semver-compare/index.js": function(exports2, module2) {
|
||||
module2.exports = function cmp(a, b) {
|
||||
var pa = a.split(".");
|
||||
var pb = b.split(".");
|
||||
for (var i = 0; i < 3; i++) {
|
||||
var na = Number(pa[i]);
|
||||
var nb = Number(pb[i]);
|
||||
if (na > nb)
|
||||
return 1;
|
||||
if (nb > na)
|
||||
return -1;
|
||||
if (!isNaN(na) && isNaN(nb))
|
||||
return 1;
|
||||
if (isNaN(na) && !isNaN(nb))
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// node_modules/please-upgrade-node/index.js
|
||||
var require_please_upgrade_node = __commonJS({
|
||||
"node_modules/please-upgrade-node/index.js": function(exports2, module2) {
|
||||
var semverCompare = require_semver_compare();
|
||||
module2.exports = function pleaseUpgradeNode2(pkg, opts) {
|
||||
var opts = opts || {};
|
||||
var requiredVersion = pkg.engines.node.replace(">=", "");
|
||||
var currentVersion = process.version.replace("v", "");
|
||||
if (semverCompare(currentVersion, requiredVersion) === -1) {
|
||||
if (opts.message) {
|
||||
console.error(opts.message(requiredVersion));
|
||||
} else {
|
||||
console.error(pkg.name + " requires at least version " + requiredVersion + " of Node, please upgrade");
|
||||
}
|
||||
if (opts.hasOwnProperty("exitCode")) {
|
||||
process.exit(opts.exitCode);
|
||||
} else {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// bin/prettier.js
|
||||
var pleaseUpgradeNode = require_please_upgrade_node();
|
||||
var packageJson = require("./package.json");
|
||||
pleaseUpgradeNode(packageJson);
|
||||
var cli = require("./cli.js");
|
||||
module.exports = cli.run(process.argv.slice(2));
|
||||
+15085
File diff suppressed because it is too large
Load Diff
+1392
File diff suppressed because one or more lines are too long
+2
File diff suppressed because one or more lines are too long
+27
File diff suppressed because one or more lines are too long
+26
File diff suppressed because one or more lines are too long
+35
File diff suppressed because one or more lines are too long
+27
File diff suppressed because one or more lines are too long
+15
File diff suppressed because one or more lines are too long
+36
File diff suppressed because one or more lines are too long
+76
File diff suppressed because one or more lines are too long
+19
File diff suppressed because one or more lines are too long
+76
File diff suppressed because one or more lines are too long
+280
File diff suppressed because one or more lines are too long
+150
File diff suppressed because one or more lines are too long
+116
File diff suppressed because one or more lines are too long
+37282
File diff suppressed because one or more lines are too long
+21
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "prettier",
|
||||
"version": "2.7.1",
|
||||
"description": "Prettier is an opinionated code formatter",
|
||||
"bin": "./bin-prettier.js",
|
||||
"repository": "prettier/prettier",
|
||||
"funding": "https://github.com/prettier/prettier?sponsor=1",
|
||||
"homepage": "https://prettier.io",
|
||||
"author": "James Long",
|
||||
"license": "MIT",
|
||||
"main": "./index.js",
|
||||
"browser": "./standalone.js",
|
||||
"unpkg": "./standalone.js",
|
||||
"engines": {
|
||||
"node": ">=10.13.0"
|
||||
},
|
||||
"files": [
|
||||
"*.js",
|
||||
"esm/*.mjs"
|
||||
]
|
||||
}
|
||||
+2
File diff suppressed because one or more lines are too long
+27
File diff suppressed because one or more lines are too long
+26
File diff suppressed because one or more lines are too long
+35
File diff suppressed because one or more lines are too long
+27
File diff suppressed because one or more lines are too long
+15
File diff suppressed because one or more lines are too long
+36
File diff suppressed because one or more lines are too long
+76
File diff suppressed because one or more lines are too long
+19
File diff suppressed because one or more lines are too long
+76
File diff suppressed because one or more lines are too long
+280
File diff suppressed because one or more lines are too long
+150
File diff suppressed because one or more lines are too long
+116
File diff suppressed because one or more lines are too long
+8978
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1 +1 @@
|
||||
d9672057404fee19f80db714ea000c61a1375776
|
||||
23a45c3c610603c5fb7b5956ee7a94657fb32b21
|
||||
+271
@@ -1,3 +1,274 @@
|
||||
## [26.5.6](https://github.com/kulshekhar/ts-jest/compare/v26.5.5...v26.5.6) (2021-05-05)
|
||||
|
||||
|
||||
### Code Refactoring
|
||||
|
||||
* refactor(config): show warning message for `sourceMap: false` ([#2557](https://github.com/kulshekhar/ts-jest/pull/2557)) ([cf60990](https://github.com/kulshekhar/ts-jest/commit/cf609900e2c5937755123bd08ca2c5f2ff5e0651)).
|
||||
|
||||
|
||||
|
||||
## [26.5.5](https://github.com/kulshekhar/ts-jest/compare/v26.5.4...v26.5.5) (2021-04-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** return file content on emitSkipped for non ts/tsx files ([#2515](https://github.com/kulshekhar/ts-jest/issues/2515)) ([0320fb3](https://github.com/kulshekhar/ts-jest/commit/0320fb3ac22056aafe4d7ae966eab84dbf23fda9)), closes [#2513](https://github.com/kulshekhar/ts-jest/issues/2513)
|
||||
|
||||
|
||||
|
||||
## [26.5.4](https://github.com/kulshekhar/ts-jest/compare/v26.5.3...v26.5.4) (2021-03-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** initialize compiler with `.ts`/`.tsx`/`.d.ts` files only ([#2457](https://github.com/kulshekhar/ts-jest/issues/2457)) ([1dc731a](https://github.com/kulshekhar/ts-jest/commit/1dc731a5faf7cda59db1cc642eb99fae973b1246)), closes [#2445](https://github.com/kulshekhar/ts-jest/issues/2445)
|
||||
|
||||
|
||||
|
||||
## [26.5.3](https://github.com/kulshekhar/ts-jest/compare/v26.5.2...v26.5.3) (2021-03-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **config:** create fallback jest config when jest config is undefined ([#2421](https://github.com/kulshekhar/ts-jest/issues/2421)) ([0fb6b45](https://github.com/kulshekhar/ts-jest/commit/0fb6b45e7dc3dd7588d27f09ac9a8849dff470cb)), closes [#2085](https://github.com/kulshekhar/ts-jest/issues/2085)
|
||||
* remove `@types/jest` from direct dep ([#2416](https://github.com/kulshekhar/ts-jest/issues/2416)) ([060302e](https://github.com/kulshekhar/ts-jest/commit/060302ed1eb8708df0acd7ab1d613ff06fc08cf3)), closes [#2406](https://github.com/kulshekhar/ts-jest/issues/2406) [#2411](https://github.com/kulshekhar/ts-jest/issues/2411)
|
||||
* **compiler:** return original file content on emit skip ([#2408](https://github.com/kulshekhar/ts-jest/issues/2408)) ([cfba8f4](https://github.com/kulshekhar/ts-jest/commit/cfba8f423dd59536d8b1e1374ef2b20bff2ed857)), closes [#2407](https://github.com/kulshekhar/ts-jest/issues/2407)
|
||||
|
||||
|
||||
|
||||
## [26.5.2](https://github.com/kulshekhar/ts-jest/compare/v26.5.1...v26.5.2) (2021-02-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** exclude files in `outDir` from compiler source files ([#2376](https://github.com/kulshekhar/ts-jest/issues/2376)) ([9034677](https://github.com/kulshekhar/ts-jest/commit/9034677f9ce0968339d3d942a70e888996fac532)), closes [#2350](https://github.com/kulshekhar/ts-jest/issues/2350) [#2374](https://github.com/kulshekhar/ts-jest/issues/2374)
|
||||
* **config:** define `matchTestFilePath` before `setupTsJestCfg` ([#2373](https://github.com/kulshekhar/ts-jest/issues/2373)) ([c427fea](https://github.com/kulshekhar/ts-jest/commit/c427fea48a24b5ce6e8b9260d3c322583b062a77)), closes [#2371](https://github.com/kulshekhar/ts-jest/issues/2371)
|
||||
* **config:** improve emit skipped error message and ensure `outDir` always `TS_JEST_OUT_DIR` ([#2357](https://github.com/kulshekhar/ts-jest/issues/2357)) ([f2808bb](https://github.com/kulshekhar/ts-jest/commit/f2808bb0b15231c67ccb9a97ed606741213c03e6))
|
||||
* **typings:** set correct typing for `tsconfig`/`tsConfig` option ([#2377](https://github.com/kulshekhar/ts-jest/issues/2377)) ([d4f6aff](https://github.com/kulshekhar/ts-jest/commit/d4f6aff3f181761bf25c64ff1a97dd19a69196f9)), closes [#2368](https://github.com/kulshekhar/ts-jest/issues/2368)
|
||||
|
||||
|
||||
|
||||
## [26.5.1](https://github.com/kulshekhar/ts-jest/compare/v26.5.0...v26.5.1) (2021-02-09)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **config:** support typed config options for jest config typescript ([#2336](https://github.com/kulshekhar/ts-jest/issues/2336)) ([f4f5d32](https://github.com/kulshekhar/ts-jest/commit/f4f5d3205d1c80e545a32c02c6a66e7e91386f7f))
|
||||
* **presets:** add typing for `presets` entry point ([#2341](https://github.com/kulshekhar/ts-jest/issues/2341)) ([e12b004](https://github.com/kulshekhar/ts-jest/commit/e12b004dcc5848d5ae0638e885147c54e11cc72b)), closes [#2325](https://github.com/kulshekhar/ts-jest/issues/2325)
|
||||
|
||||
|
||||
|
||||
# [26.5.0](https://github.com/kulshekhar/ts-jest/compare/v26.5.0...v26.4.4) (2021-01-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* reduce size of `node_modules` when adding `ts-jest` ([#2309](https://github.com/kulshekhar/ts-jest/issues/2309)) ([6bf2e8a](https://github.com/kulshekhar/ts-jest/commit/b8d5d2090567f23947d9efd87f5f869b16bf2e8a))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* introduce `exclude` to exclude files from diagnostics ([#2308](https://github.com/kulshekhar/ts-jest/issues/2308)) ([cd82fd3](https://github.com/kulshekhar/ts-jest/commit/0c555c250774a7fd9e356cf20a3d8b693cd82fd3))
|
||||
|
||||
|
||||
### DEPRECATIONS
|
||||
|
||||
* **config**: deprecate `pathRegex` in favor of `exclude` ([#2308](https://github.com/kulshekhar/ts-jest/issues/2308)) ([cd82fd3](https://github.com/kulshekhar/ts-jest/commit/0c555c250774a7fd9e356cf20a3d8b693cd82fd3))
|
||||
|
||||
|
||||
|
||||
## [26.4.4](https://github.com/kulshekhar/ts-jest/compare/v26.4.3...v26.4.4) (2020-11-08)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* revert usage of `@jest/create-cache-key-function` ([#2108](https://github.com/kulshekhar/ts-jest/issues/2108)) ([dee8231](https://github.com/kulshekhar/ts-jest/commit/dee823172ce1e8eb9e0b2dd3aeed1ab4033bd0d9)), closes [#2080](https://github.com/kulshekhar/ts-jest/issues/2080) [#2090](https://github.com/kulshekhar/ts-jest/issues/2090) [#2104](https://github.com/kulshekhar/ts-jest/issues/2104)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **testing:** expose all types for util `mocked` ([#2096](https://github.com/kulshekhar/ts-jest/issues/2096)) ([b1d072b](https://github.com/kulshekhar/ts-jest/commit/b1d072b52b9a7665b3a6914b0895f84f6ee3f8c0)), closes [#2086](https://github.com/kulshekhar/ts-jest/issues/2086)
|
||||
|
||||
|
||||
|
||||
## [26.4.3](https://github.com/kulshekhar/ts-jest/compare/v26.4.2...v26.4.3) (2020-10-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** only exclude test files when initializing compiler ([#2062](https://github.com/kulshekhar/ts-jest/issues/2062)) ([7264c13](https://github.com/kulshekhar/ts-jest/commit/7264c137114b6dd895624e3476dd7ec57b64ee13)), closes [#2061](https://github.com/kulshekhar/ts-jest/issues/2061), [#2068](https://github.com/kulshekhar/ts-jest/issues/2068), [#2072](https://github.com/kulshekhar/ts-jest/issues/2072), [#2073](https://github.com/kulshekhar/ts-jest/issues/2073)
|
||||
* **config:** resolve `.babelrc` file path before attempting to read it ([#2071](https://github.com/kulshekhar/ts-jest/issues/2071)) ([681bfef](https://github.com/kulshekhar/ts-jest/commit/681bfef41744f09cd50b71072f4d001cb58da82e)), closes [#2064](https://github.com/kulshekhar/ts-jest/issues/2064)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **config:** allow to override resolve tsconfig behavior ([#2063](https://github.com/kulshekhar/ts-jest/issues/2063)) ([9f46ace](https://github.com/kulshekhar/ts-jest/commit/9f46acefceb1fa71ee2e8b3b3c172ceb0544b4c4))
|
||||
|
||||
|
||||
|
||||
## [26.4.2](https://github.com/kulshekhar/ts-jest/compare/v26.4.1...v26.4.2) (2020-10-23)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **config:** expose several typings as public apis ([#2054](https://github.com/kulshekhar/ts-jest/issues/2054)) ([3b6b705](https://github.com/kulshekhar/ts-jest/commit/3b6b7055e2b9c74e81fb91596c807ace02ab77a1))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **compiler:** speed up bootstrap time for `isolatedModules:false` ([#2055](https://github.com/kulshekhar/ts-jest/issues/2055)) ([230b5dd](https://github.com/kulshekhar/ts-jest/commit/230b5ddbee55357d25dd190cd45aa8a30d7f31e0))
|
||||
|
||||
|
||||
### DEPRECATIONS
|
||||
|
||||
* **config**: deprecate `tsConfig` in favor of `tsconfig` ([#1997](https://github.com/kulshekhar/ts-jest/pull/1997))
|
||||
* **config**: deprecate `packageJson` since internal codes don't use it anymore ([#2034](https://github.com/kulshekhar/ts-jest/pull/2034))
|
||||
|
||||
|
||||
|
||||
## [26.4.1](https://github.com/kulshekhar/ts-jest/compare/v26.4.0...v26.4.1) (2020-09-29)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **utils:** `MaybeMockedConstructor` returns T ([#1976](https://github.com/kulshekhar/ts-jest/issues/1976)) ([b7712b2](https://github.com/kulshekhar/ts-jest/commit/b7712b2055d8f32dd97999de1d94e8f3515d79e8))
|
||||
* **utils:** revert `path.join` and add check on prefix ends with `/` ([#1989](https://github.com/kulshekhar/ts-jest/issues/1989)) ([3d9035b](https://github.com/kulshekhar/ts-jest/commit/3d9035bd70dc087d4c5a943bb2fe2af2d0822875)), closes [#1982](https://github.com/kulshekhar/ts-jest/issues/1982)
|
||||
|
||||
|
||||
|
||||
# [26.4.0](https://github.com/kulshekhar/ts-jest/compare/v26.3.0...v26.4.0) (2020-09-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **utils:** `pathsToModuleNameMapper` resolve path mapping with `path.join` ([#1969](https://github.com/kulshekhar/ts-jest/issues/1969)) ([81fce4c](https://github.com/kulshekhar/ts-jest/commit/81fce4c090811a1cc071579a99dc193fb976b117)), closes [#1968](https://github.com/kulshekhar/ts-jest/issues/1968)
|
||||
* set minimum `jest-util` version at 26.1.0 ([#1914](https://github.com/kulshekhar/ts-jest/issues/1914)) ([f00414c](https://github.com/kulshekhar/ts-jest/commit/f00414c6fbf8fc5413fd33d0a271c4a164c50d45)), closes [#1913](https://github.com/kulshekhar/ts-jest/issues/1913)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **config:** allow custom options in custom transformers ([#1966](https://github.com/kulshekhar/ts-jest/issues/1966)) ([1452ce4](https://github.com/kulshekhar/ts-jest/commit/1452ce4afcd36049cddd0db0861f1ac26b66f8c1)), closes [#1942](https://github.com/kulshekhar/ts-jest/issues/1942)
|
||||
* **transformers:** support hoisting when using `@jest/globals` ([#1937](https://github.com/kulshekhar/ts-jest/issues/1937)) ([0e5be15](https://github.com/kulshekhar/ts-jest/commit/0e5be1597d755fed11869f67df05eeea54b3106f)), closes [#1593](https://github.com/kulshekhar/ts-jest/issues/1593)
|
||||
* **transformers:** add `path-mapping` custom AST transformer ([#1927](https://github.com/kulshekhar/ts-jest/issues/1927)) ([3325186](https://github.com/kulshekhar/ts-jest/commit/3325186b6e55f41eb9bf7d81e092a358fc402b13))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **compiler:** remove `createProgram` for `isolatedModules: true` to boost startup speed ([#1941](https://github.com/kulshekhar/ts-jest/issues/1941)) ([dd84534](https://github.com/kulshekhar/ts-jest/commit/dd8453401840862186f991e2d514e0d328a67987))
|
||||
|
||||
|
||||
|
||||
# [26.3.0](https://github.com/kulshekhar/ts-jest/compare/v26.2.0...v26.3.0) (2020-08-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **config:** compute cache key without reading `package.json` ([#1893](https://github.com/kulshekhar/ts-jest/issues/1893)) ([4875a58](https://github.com/kulshekhar/ts-jest/commit/4875a58345666e0407f9f5b3f95049ae2c9d056d)), closes [#1892](https://github.com/kulshekhar/ts-jest/issues/1892)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* support TypeScript 4.0 ([#1889](https://github.com/kulshekhar/ts-jest/issues/1889)) ([f070e93](https://github.com/kulshekhar/ts-jest/commit/f070e9334a9cf31fa6f0d73b3f69d805be72601d))
|
||||
|
||||
|
||||
|
||||
# [26.2.0](https://github.com/kulshekhar/ts-jest/compare/v26.1.4...v26.2.0) (2020-08-11)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* move `@types/jest` to dependencies to work well with yarn 2 ([#1859](https://github.com/kulshekhar/ts-jest/issues/1859)) ([5eb1389](https://github.com/kulshekhar/ts-jest/commit/5eb1389caaa0431e49ae6ca26b18e290208e0a0a)), closes [#1857](https://github.com/kulshekhar/ts-jest/issues/1857)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **config:** support `after` and `afterDeclarations` AST transformers ([#1831](https://github.com/kulshekhar/ts-jest/issues/1831)) ([be20a7c](https://github.com/kulshekhar/ts-jest/commit/be20a7c78c97027b33aec178da0f533095790871))
|
||||
* allow opt-out version warning message by environment variable `TS_JEST_DISABLE_VER_CHECKER` ([#1821](https://github.com/kulshekhar/ts-jest/issues/1821)) ([e6b42fc](https://github.com/kulshekhar/ts-jest/commit/e6b42fcd7a75c7b14e636a45cda04de18a46908b)), closes [#1774](https://github.com/kulshekhar/ts-jest/issues/1774)
|
||||
|
||||
|
||||
|
||||
## [26.1.4](https://github.com/kulshekhar/ts-jest/compare/v26.1.3...v26.1.4) (2020-07-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** check if test file exists before doing type check ([#1827](https://github.com/kulshekhar/ts-jest/issues/1827)) ([cc89d5b](https://github.com/kulshekhar/ts-jest/commit/cc89d5b1f912975cd29114c5b3b0bf18426816da)), closes [#1506](https://github.com/kulshekhar/ts-jest/issues/1506)
|
||||
|
||||
|
||||
|
||||
## [26.1.3](https://github.com/kulshekhar/ts-jest/compare/v26.1.2...v26.1.3) (2020-07-16)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* revert [#1793](https://github.com/kulshekhar/ts-jest/issues/1793) ([#1804](https://github.com/kulshekhar/ts-jest/issues/1804)) ([5095525](https://github.com/kulshekhar/ts-jest/commit/5095525333c8579c9c5e7f3149294b31f28d6774))
|
||||
|
||||
|
||||
|
||||
## [26.1.2](https://github.com/kulshekhar/ts-jest/compare/v26.1.1...v26.1.2) (2020-07-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** use `resolveModuleNames` TypeScript API to get resolved modules for test files ([#1784](https://github.com/kulshekhar/ts-jest/issues/1784)) ([5f26054](https://github.com/kulshekhar/ts-jest/commit/5f2605457e94b548bd7b9b28fc968554f7eefa91)), closes [#1747](https://github.com/kulshekhar/ts-jest/issues/1747)
|
||||
* **config:** invalidate cache when other options in `tsconfig` change ([#1788](https://github.com/kulshekhar/ts-jest/issues/1788)) ([6948855](https://github.com/kulshekhar/ts-jest/commit/69488552eca2846f3fc6ba86ab49d7893caaf521))
|
||||
|
||||
|
||||
### Performance Improvements
|
||||
|
||||
* **compiler:** cache module resolution for `isolatedModules: false` ([#1786](https://github.com/kulshekhar/ts-jest/issues/1786)) ([7f731ed](https://github.com/kulshekhar/ts-jest/commit/7f731ed8a02755aeb41ecb27df4eaf16db2ddd95))
|
||||
* **compiler:** use `globsToMatcher` from `jest-util` ([#1754](https://github.com/kulshekhar/ts-jest/issues/1754)) ([44f3913](https://github.com/kulshekhar/ts-jest/commit/44f3913c2a017734ed87346b1c5fbec639d02062))
|
||||
|
||||
|
||||
|
||||
<a name="26.1.1"></a>
|
||||
## [26.1.1](https://github.com/kulshekhar/ts-jest/compare/v26.1.0...v26.1.1) (2020-06-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** generate source map correctly when tsconfig `mapRoot` is set ([#1741](https://github.com/kulshekhar/ts-jest/issues/1741)) ([01ac417](https://github.com/kulshekhar/ts-jest/commit/01ac417)), closes [#1718](https://github.com/kulshekhar/ts-jest/issues/1718)
|
||||
* **config:** show version warning when using ts-jest without babel ([#1729](https://github.com/kulshekhar/ts-jest/issues/1729)) ([e512bc0](https://github.com/kulshekhar/ts-jest/commit/e512bc0)), fixes [#1678-issuecomment-641930332](https://github.com//github.com/kulshekhar/ts-jest/pull/1678/issues/issuecomment-641930332), [#1678-issuecomment-639528993](https://github.com//github.com/kulshekhar/ts-jest/pull/1678/issues/issuecomment-639528993)
|
||||
|
||||
|
||||
|
||||
<a name="26.1.0"></a>
|
||||
# [26.1.0](https://github.com/kulshekhar/ts-jest/compare/v26.0.0...v26.1.0) (2020-05-30)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **typing:** don't mark `BabelConfig` as internal type ([#1667](https://github.com/kulshekhar/ts-jest/issues/1667)) ([558c307](https://github.com/kulshekhar/ts-jest/commit/558c307)), closes [#1666](https://github.com/kulshekhar/ts-jest/issues/1666)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **config:** show a warning message when TypeScript `target` version doesn't match with recommended NodeJs version ([#1678](https://github.com/kulshekhar/ts-jest/issues/1678)) ([085bdf5](https://github.com/kulshekhar/ts-jest/commit/085bdf5))
|
||||
* **config:** support multiple paths for `pathsToModuleNameMapper` ([#1690](https://github.com/kulshekhar/ts-jest/issues/1690)) ([a727bd5](https://github.com/kulshekhar/ts-jest/commit/a727bd5))
|
||||
* support TypeScript 3.9 ([#1653](https://github.com/kulshekhar/ts-jest/issues/1653)) ([fc3d5ad](https://github.com/kulshekhar/ts-jest/commit/fc3d5ad))
|
||||
|
||||
|
||||
|
||||
<a name="26.0.0"></a>
|
||||
# [26.0.0](https://github.com/kulshekhar/ts-jest/compare/v25.5.1...v26.0.0) (2020-05-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** return `undefined` for `getScriptVersion` when a file doesn't exist in memory cache ([#1641](https://github.com/kulshekhar/ts-jest/issues/1641)) ([6851b8e](https://github.com/kulshekhar/ts-jest/commit/6851b8e))
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* support Jest v26 ([#1602](https://github.com/kulshekhar/ts-jest/issues/1602)) ([23b7741](https://github.com/kulshekhar/ts-jest/commit/23b7741))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* Requires a minimum of TypeScript v3.8
|
||||
* Drop support for Node 8
|
||||
|
||||
|
||||
<a name="25.5.1"></a>
|
||||
## [25.5.1](https://github.com/kulshekhar/ts-jest/compare/v25.5.0...v25.5.1) (2020-05-09)
|
||||
|
||||
|
||||
+12
-11
@@ -1,18 +1,19 @@
|
||||
# ts-jest
|
||||
<h1 align="center">ts-jest</h1>
|
||||
|
||||
[](https://badge.fury.io/js/ts-jest)
|
||||
[](https://npmjs.org/package/ts-jest)
|
||||
[](https://snyk.io/test/github/kulshekhar/ts-jest)
|
||||
[](https://coveralls.io/github/kulshekhar/ts-jest?branch=master)
|
||||
[](https://dependabot.com)
|
||||
[](https://travis-ci.com/kulshekhar/ts-jest)
|
||||
[](https://github.com/kulshekhar/ts-jest/actions)
|
||||
[](https://github.com/kulshekhar/ts-jest/actions)
|
||||
<p align="center">A TypeScript preprocessor with source map support for Jest that lets you use Jest to test projects written in TypeScript.</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/package/ts-jest"><img src="https://img.shields.io/npm/v/ts-jest/latest.svg?style=flat-square" alt="NPM version" /> </a>
|
||||
<a href="https://www.npmjs.com/package/ts-jest"><img src="https://img.shields.io/npm/dm/ts-jest.svg?style=flat-square" alt="NPM downloads"/> </a>
|
||||
<a href="https://snyk.io/test/github/kulshekhar/ts-jest"><img src="https://snyk.io/test/github/kulshekhar/ts-jest/badge.svg?style=flat-square" alt="Known vulnerabilities"/> </a>
|
||||
<a href="https://coveralls.io/github/kulshekhar/ts-jest?branch=master"><img src="https://coveralls.io/repos/github/kulshekhar/ts-jest/badge.svg?branch=master" alt="Coverage status"/> </a>
|
||||
<a href="https://travis-ci.com/kulshekhar/ts-jest"><img src="https://travis-ci.com/kulshekhar/ts-jest.svg?branch=master" alt="Build status"/> </a>
|
||||
<a href="https://actions-badge.atrox.dev/kulshekhar/ts-jest/goto?ref=master"><img alt="GitHub actions" src="https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fkulshekhar%2Fts-jest%2Fbadge%3Fref%3Dmaster&style=flat-square" /> </a>
|
||||
<a href="https://github.com/kulshekhar/ts-jest/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/ts-jest.svg?style=flat-square" alt="GitHub license"/> </a>
|
||||
</p>
|
||||
|
||||
<img src="./icon.png" align="right" title="ts-jest Logo" width="128" height="128">
|
||||
|
||||
**`ts-jest`** is a TypeScript preprocessor with source map support for Jest that lets you use Jest to test projects written in TypeScript.
|
||||
|
||||
It supports all features of TypeScript including type-checking. [Read more about Babel7 + `preset-typescript` **vs** TypeScript (and `ts-jest`)](https://kulshekhar.github.io/ts-jest/user/babel7-or-ts).
|
||||
|
||||
---
|
||||
|
||||
+4
-2
@@ -47,14 +47,16 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.help = exports.run = void 0;
|
||||
var fs_1 = require("fs");
|
||||
var json5_1 = require("json5");
|
||||
var path_1 = require("path");
|
||||
var presets_1 = require("../helpers/presets");
|
||||
exports.run = function (args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var file, filePath, name, isPackage, exists, pkgFile, hasPackage, _a, jestPreset, askedTsconfig, force, jsdom, tsconfig, pkgJson, jsFilesProcessor, shouldPostProcessWithBabel, preset, body, base, tsJestConf, content;
|
||||
return __generator(this, function (_b) {
|
||||
file = args._[0] || 'jest.config.js';
|
||||
var _b, _c;
|
||||
return __generator(this, function (_d) {
|
||||
file = (_c = (_b = args._[0]) === null || _b === void 0 ? void 0 : _b.toString()) !== null && _c !== void 0 ? _c : 'jest.config.js';
|
||||
filePath = path_1.join(process.cwd(), file);
|
||||
name = path_1.basename(file);
|
||||
isPackage = name === 'package.json';
|
||||
|
||||
+5
-3
@@ -56,18 +56,20 @@ var __spread = (this && this.__spread) || function () {
|
||||
return ar;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.help = exports.run = void 0;
|
||||
var bs_logger_1 = require("bs-logger");
|
||||
var stableStringify = require("fast-json-stable-stringify");
|
||||
var fs_1 = require("fs");
|
||||
var json5_1 = require("json5");
|
||||
var path_1 = require("path");
|
||||
var backports_1 = require("../../util/backports");
|
||||
var backports_1 = require("../../utils/backports");
|
||||
var presets_1 = require("../helpers/presets");
|
||||
exports.run = function (args) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
var nullLogger, file, filePath, footNotes, name, isPackage, actualConfig, migratedConfig, presetName, preset, jsTransformers, jsWithTs, jsWithBabel, presetValue, migratedValue, presetValue, migratedValue, before, after, stringify, prefix;
|
||||
return __generator(this, function (_a) {
|
||||
var _a;
|
||||
return __generator(this, function (_b) {
|
||||
nullLogger = bs_logger_1.createLogger({ targets: [] });
|
||||
file = args._[0];
|
||||
file = (_a = args._[0]) === null || _a === void 0 ? void 0 : _a.toString();
|
||||
filePath = path_1.resolve(process.cwd(), file);
|
||||
footNotes = [];
|
||||
if (!fs_1.existsSync(filePath)) {
|
||||
|
||||
+1
@@ -36,6 +36,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
}
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.help = exports.run = void 0;
|
||||
exports.run = function (_) { return __awaiter(void 0, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
process.stdout.write("\nUsage:\n ts-jest command [options] [...args]\n\nCommands:\n config:init Creates initial Jest configuration\n config:migrate Migrates a given Jest configuration\n help [command] Show this help, or help about a command\n\nExample:\n ts-jest help config:migrate\n");
|
||||
|
||||
+6
-4
@@ -1,5 +1,6 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.jsWIthBabel = exports.jsWithTs = exports.defaults = exports.allPresets = void 0;
|
||||
var definePreset = function (fullName) { return ({
|
||||
fullName: fullName,
|
||||
get name() {
|
||||
@@ -11,10 +12,11 @@ var definePreset = function (fullName) { return ({
|
||||
get jsVarName() {
|
||||
return this.isDefault
|
||||
? 'defaults'
|
||||
: fullName
|
||||
.split('/')
|
||||
.pop()
|
||||
.replace(/\-([a-z])/g, function (_, l) { return l.toUpperCase(); });
|
||||
:
|
||||
fullName
|
||||
.split('/')
|
||||
.pop()
|
||||
.replace(/\-([a-z])/g, function (_, l) { return l.toUpperCase(); });
|
||||
},
|
||||
get value() {
|
||||
return require("../../../" + fullName.replace(/^ts-jest\//, '') + "/jest-preset");
|
||||
|
||||
+7
-3
@@ -35,18 +35,22 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
var _a;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.processArgv = void 0;
|
||||
var bs_logger_1 = require("bs-logger");
|
||||
var yargsParser = require("yargs-parser");
|
||||
var logger_1 = require("../util/logger");
|
||||
var yargs_parser_1 = __importDefault(require("yargs-parser"));
|
||||
var logger_1 = require("../utils/logger");
|
||||
var VALID_COMMANDS = ['help', 'config:migrate', 'config:init'];
|
||||
var logger = logger_1.rootLogger.child((_a = {}, _a[bs_logger_1.LogContexts.namespace] = 'cli', _a[bs_logger_1.LogContexts.application] = 'ts-jest', _a));
|
||||
function cli(args) {
|
||||
return __awaiter(this, void 0, void 0, function () {
|
||||
var parsedArgv, command, isHelp, _a, run, help, cmd;
|
||||
return __generator(this, function (_b) {
|
||||
parsedArgv = yargsParser(args, {
|
||||
parsedArgv = yargs_parser_1.default(args, {
|
||||
boolean: ['dry-run', 'jest-preset', 'allow-js', 'diff', 'babel', 'force', 'jsdom'],
|
||||
string: ['tsconfig', 'js'],
|
||||
count: ['verbose'],
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
export {};
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var fs_1 = require("fs");
|
||||
var micromatch = require("micromatch");
|
||||
var path_1 = require("path");
|
||||
var sha1_1 = require("../util/sha1");
|
||||
function getResolvedModulesCache(cacheDir) {
|
||||
return path_1.join(cacheDir, sha1_1.sha1('ts-jest-resolved-modules', '\x00'));
|
||||
}
|
||||
exports.getResolvedModulesCache = getResolvedModulesCache;
|
||||
function cacheResolvedModules(fileName, fileContent, memoryCache, program, cacheDir, logger) {
|
||||
var importReferences = program.getSourceFile(fileName).imports;
|
||||
if (importReferences.length) {
|
||||
logger.debug({ fileName: fileName }, 'cacheResolvedModules(): get resolved modules');
|
||||
memoryCache.resolvedModules[fileName] = Object.create(null);
|
||||
memoryCache.resolvedModules[fileName].modulePaths = importReferences
|
||||
.filter(function (importReference) { var _a; return (_a = importReference.parent.parent.resolvedModules) === null || _a === void 0 ? void 0 : _a.get(importReference.text); })
|
||||
.map(function (importReference) {
|
||||
return path_1.normalize(importReference.parent.parent.resolvedModules.get(importReference.text)
|
||||
.resolvedFileName);
|
||||
})
|
||||
.reduce(function (a, b) { return a.concat(b); }, []);
|
||||
memoryCache.resolvedModules[fileName].testFileContent = fileContent;
|
||||
fs_1.writeFileSync(getResolvedModulesCache(cacheDir), JSON.stringify(memoryCache.resolvedModules));
|
||||
}
|
||||
}
|
||||
exports.cacheResolvedModules = cacheResolvedModules;
|
||||
function isTestFile(testMatchPatterns, fileName) {
|
||||
return testMatchPatterns.some(function (pattern) {
|
||||
return typeof pattern === 'string' ? micromatch.isMatch(fileName, pattern) : pattern.test(fileName);
|
||||
});
|
||||
}
|
||||
exports.isTestFile = isTestFile;
|
||||
+2
-1
@@ -1 +1,2 @@
|
||||
export {};
|
||||
export declare const SOURCE_MAPPING_PREFIX = "sourceMappingURL=";
|
||||
export declare function updateOutput(outputText: string, normalizedFileName: string, sourceMap: string): string;
|
||||
|
||||
+15
-56
@@ -1,15 +1,4 @@
|
||||
"use strict";
|
||||
var __assign = (this && this.__assign) || function () {
|
||||
__assign = Object.assign || function(t) {
|
||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||
s = arguments[i];
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||
t[p] = s[p];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
return __assign.apply(this, arguments);
|
||||
};
|
||||
var __read = (this && this.__read) || function (o, n) {
|
||||
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
||||
if (!m) return o;
|
||||
@@ -27,70 +16,40 @@ var __read = (this && this.__read) || function (o, n) {
|
||||
return ar;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var fs_1 = require("fs");
|
||||
var mkdirp = require("mkdirp");
|
||||
var path_1 = require("path");
|
||||
var compiler_utils_1 = require("./compiler-utils");
|
||||
exports.createCompilerInstance = exports.updateOutput = exports.SOURCE_MAPPING_PREFIX = void 0;
|
||||
var language_service_1 = require("./language-service");
|
||||
var transpiler_1 = require("./transpiler");
|
||||
function updateOutput(outputText, normalizedFileName, sourceMap, getExtension) {
|
||||
var base = path_1.basename(normalizedFileName);
|
||||
var json_1 = require("../utils/json");
|
||||
exports.SOURCE_MAPPING_PREFIX = 'sourceMappingURL=';
|
||||
function updateOutput(outputText, normalizedFileName, sourceMap) {
|
||||
var base64Map = Buffer.from(updateSourceMap(sourceMap, normalizedFileName), 'utf8').toString('base64');
|
||||
var sourceMapContent = "data:application/json;charset=utf-8;base64," + base64Map;
|
||||
var sourceMapLength = (base + ".map").length + (getExtension(normalizedFileName).length - path_1.extname(normalizedFileName).length);
|
||||
return outputText.slice(0, -sourceMapLength) + sourceMapContent;
|
||||
return (outputText.slice(0, outputText.lastIndexOf(exports.SOURCE_MAPPING_PREFIX) + exports.SOURCE_MAPPING_PREFIX.length) + sourceMapContent);
|
||||
}
|
||||
exports.updateOutput = updateOutput;
|
||||
var updateSourceMap = function (sourceMapText, normalizedFileName) {
|
||||
var sourceMap = JSON.parse(sourceMapText);
|
||||
sourceMap.file = normalizedFileName;
|
||||
sourceMap.sources = [normalizedFileName];
|
||||
delete sourceMap.sourceRoot;
|
||||
return JSON.stringify(sourceMap);
|
||||
return json_1.stringify(sourceMap);
|
||||
};
|
||||
var compileAndCacheResult = function (memoryCache, compileFn, getExtension, logger) { return function (code, fileName, lineOffset) {
|
||||
logger.debug({ fileName: fileName }, 'compileAndCacheResult(): get compile output');
|
||||
var compileAndUpdateOutput = function (compileFn, logger) { return function (code, fileName, lineOffset) {
|
||||
logger.debug({ fileName: fileName }, 'compileAndUpdateOutput(): get compile output');
|
||||
var _a = __read(compileFn(code, fileName, lineOffset), 2), value = _a[0], sourceMap = _a[1];
|
||||
var output = updateOutput(value, fileName, sourceMap, getExtension);
|
||||
memoryCache.files.set(fileName, __assign(__assign({}, memoryCache.files.get(fileName)), { output: output }));
|
||||
return output;
|
||||
return updateOutput(value, fileName, sourceMap);
|
||||
}; };
|
||||
exports.createCompilerInstance = function (configs) {
|
||||
var logger = configs.logger.child({ namespace: 'ts-compiler' });
|
||||
var compilerOptions = configs.parsedTsConfig.options, tsJest = configs.tsJest;
|
||||
var cacheDir = configs.tsCacheDir;
|
||||
var ts = configs.compilerModule;
|
||||
var compilerOptions = configs.parsedTsConfig.options;
|
||||
var extensions = ['.ts', '.tsx'];
|
||||
var memoryCache = {
|
||||
files: new Map(),
|
||||
resolvedModules: Object.create(null),
|
||||
};
|
||||
if (compilerOptions.allowJs) {
|
||||
extensions.push('.js');
|
||||
extensions.push('.jsx');
|
||||
}
|
||||
if (cacheDir) {
|
||||
mkdirp.sync(cacheDir);
|
||||
try {
|
||||
var fsMemoryCache = fs_1.readFileSync(compiler_utils_1.getResolvedModulesCache(cacheDir), 'utf-8');
|
||||
memoryCache.resolvedModules = JSON.parse(fsMemoryCache);
|
||||
}
|
||||
catch (e) { }
|
||||
}
|
||||
configs.parsedTsConfig.fileNames.forEach(function (fileName) {
|
||||
memoryCache.files.set(fileName, {
|
||||
version: 0,
|
||||
});
|
||||
});
|
||||
var getExtension = compilerOptions.jsx === ts.JsxEmit.Preserve
|
||||
? function (path) { return (/\.[tj]sx$/.test(path) ? '.jsx' : '.js'); }
|
||||
: function (_) { return '.js'; };
|
||||
var compilerInstance;
|
||||
if (!tsJest.isolatedModules) {
|
||||
compilerInstance = language_service_1.initializeLanguageServiceInstance(configs, memoryCache, logger);
|
||||
}
|
||||
else {
|
||||
compilerInstance = transpiler_1.initializeTranspilerInstance(configs, memoryCache, logger);
|
||||
}
|
||||
var compile = compileAndCacheResult(memoryCache, compilerInstance.compileFn, getExtension, logger);
|
||||
var compilerInstance = !configs.isolatedModules
|
||||
? language_service_1.initializeLanguageServiceInstance(configs, logger)
|
||||
: transpiler_1.initializeTranspilerInstance(configs, logger);
|
||||
var compile = compileAndUpdateOutput(compilerInstance.compileFn, logger);
|
||||
return { cwd: configs.cwd, compile: compile, program: compilerInstance.program };
|
||||
};
|
||||
|
||||
+161
-30
@@ -1,4 +1,40 @@
|
||||
"use strict";
|
||||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
||||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
||||
return new (P || (P = Promise))(function (resolve, reject) {
|
||||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
||||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
||||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
||||
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
||||
});
|
||||
};
|
||||
var __generator = (this && this.__generator) || function (thisArg, body) {
|
||||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
|
||||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
||||
function verb(n) { return function (v) { return step([n, v]); }; }
|
||||
function step(op) {
|
||||
if (f) throw new TypeError("Generator is already executing.");
|
||||
while (_) try {
|
||||
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
||||
if (y = 0, t) op = [op[0] & 2, t.value];
|
||||
switch (op[0]) {
|
||||
case 0: case 1: t = op; break;
|
||||
case 4: _.label++; return { value: op[1], done: false };
|
||||
case 5: _.label++; y = op[1]; op = [0]; continue;
|
||||
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
||||
default:
|
||||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
||||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
||||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
||||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
||||
if (t[2]) _.ops.pop();
|
||||
_.trys.pop(); continue;
|
||||
}
|
||||
op = body.call(thisArg, _);
|
||||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
||||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
||||
}
|
||||
};
|
||||
var __read = (this && this.__read) || function (o, n) {
|
||||
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
||||
if (!m) return o;
|
||||
@@ -19,35 +55,100 @@ var __spread = (this && this.__spread) || function () {
|
||||
for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
|
||||
return ar;
|
||||
};
|
||||
var __values = (this && this.__values) || function(o) {
|
||||
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
||||
if (m) return m.call(o);
|
||||
if (o && typeof o.length === "number") return {
|
||||
next: function () {
|
||||
if (o && i >= o.length) o = void 0;
|
||||
return { value: o && o[i++], done: !o };
|
||||
}
|
||||
};
|
||||
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.initializeLanguageServiceInstance = void 0;
|
||||
var bs_logger_1 = require("bs-logger");
|
||||
var fs_1 = require("fs");
|
||||
var path_1 = require("path");
|
||||
var memoize = require("lodash/memoize");
|
||||
var mkdirp = require("mkdirp");
|
||||
var constants_1 = require("../constants");
|
||||
var messages_1 = require("../util/messages");
|
||||
var compiler_utils_1 = require("./compiler-utils");
|
||||
var memoize = require("lodash.memoize");
|
||||
function doTypeChecking(configs, fileName, service, logger) {
|
||||
if (configs.shouldReportDiagnostic(fileName)) {
|
||||
var messages_1 = require("../utils/messages");
|
||||
var json_1 = require("../utils/json");
|
||||
var sha1_1 = require("../utils/sha1");
|
||||
function doTypeChecking(configs, diagnosedFiles, fileName, service, logger) {
|
||||
if (configs.shouldReportDiagnostics(fileName)) {
|
||||
var diagnostics = service.getSemanticDiagnostics(fileName).concat(service.getSyntacticDiagnostics(fileName));
|
||||
diagnosedFiles.push(fileName);
|
||||
configs.raiseDiagnostics(diagnostics, fileName, logger);
|
||||
}
|
||||
}
|
||||
exports.initializeLanguageServiceInstance = function (configs, memoryCache, logger) {
|
||||
exports.initializeLanguageServiceInstance = function (configs, logger) {
|
||||
var _a;
|
||||
logger.debug('initializeLanguageServiceInstance(): create typescript compiler');
|
||||
var ts = configs.compilerModule;
|
||||
var cwd = configs.cwd;
|
||||
var cacheDir = configs.tsCacheDir;
|
||||
var _b = configs.parsedTsConfig, options = _b.options, fileNames = _b.fileNames;
|
||||
var diagnosedFiles = [];
|
||||
var serviceHostTraceCtx = (_a = {
|
||||
namespace: 'ts:serviceHost',
|
||||
call: null
|
||||
},
|
||||
_a[bs_logger_1.LogContexts.logLevel] = bs_logger_1.LogLevels.trace,
|
||||
_a);
|
||||
var memoryCache = {
|
||||
files: new Map(),
|
||||
resolvedModules: new Map(),
|
||||
};
|
||||
var tsResolvedModulesCachePath;
|
||||
if (cacheDir) {
|
||||
mkdirp.sync(cacheDir);
|
||||
tsResolvedModulesCachePath = path_1.join(cacheDir, sha1_1.sha1('ts-jest-resolved-modules', '\x00'));
|
||||
try {
|
||||
var cachedTSResolvedModules = fs_1.readFileSync(tsResolvedModulesCachePath, 'utf-8');
|
||||
memoryCache.resolvedModules = new Map(json_1.parse(cachedTSResolvedModules));
|
||||
}
|
||||
catch (e) { }
|
||||
}
|
||||
configs.parsedTsConfig.fileNames
|
||||
.filter(function (fileName) { return constants_1.TS_TSX_REGEX.test(path_1.extname(fileName)) && !configs.isTestFile(fileName); })
|
||||
.forEach(function (fileName) {
|
||||
memoryCache.files.set(fileName, {
|
||||
version: 0,
|
||||
});
|
||||
});
|
||||
function isFileInCache(fileName) {
|
||||
return memoryCache.files.has(fileName) && memoryCache.files.get(fileName).version !== 0;
|
||||
}
|
||||
var cacheReadFile = logger.wrap(serviceHostTraceCtx, 'readFile', memoize(ts.sys.readFile));
|
||||
var moduleResolutionHost = {
|
||||
fileExists: memoize(ts.sys.fileExists),
|
||||
readFile: cacheReadFile,
|
||||
directoryExists: memoize(ts.sys.directoryExists),
|
||||
getCurrentDirectory: function () { return cwd; },
|
||||
realpath: ts.sys.realpath && memoize(ts.sys.realpath),
|
||||
getDirectories: memoize(ts.sys.getDirectories),
|
||||
};
|
||||
var moduleResolutionCache = ts.createModuleResolutionCache(cwd, function (x) { return x; }, options);
|
||||
function resolveModuleNames(moduleNames, containingFile) {
|
||||
var _a;
|
||||
var normalizedContainingFile = path_1.normalize(containingFile);
|
||||
var currentResolvedModules = (_a = memoryCache.resolvedModules.get(normalizedContainingFile)) !== null && _a !== void 0 ? _a : [];
|
||||
return moduleNames.map(function (moduleName) {
|
||||
var resolveModuleName = ts.resolveModuleName(moduleName, containingFile, options, moduleResolutionHost, moduleResolutionCache);
|
||||
var resolvedModule = resolveModuleName.resolvedModule;
|
||||
if (configs.isTestFile(normalizedContainingFile) && resolvedModule) {
|
||||
var normalizedResolvedFileName = path_1.normalize(resolvedModule.resolvedFileName);
|
||||
if (!currentResolvedModules.includes(normalizedResolvedFileName)) {
|
||||
currentResolvedModules.push(normalizedResolvedFileName);
|
||||
memoryCache.resolvedModules.set(normalizedContainingFile, currentResolvedModules);
|
||||
}
|
||||
}
|
||||
return resolvedModule;
|
||||
});
|
||||
}
|
||||
var projectVersion = 1;
|
||||
var updateMemoryCache = function (contents, fileName) {
|
||||
logger.debug({ fileName: fileName }, 'updateMemoryCache(): update memory cache for language service');
|
||||
@@ -81,18 +182,19 @@ exports.initializeLanguageServiceInstance = function (configs, memoryCache, logg
|
||||
getProjectVersion: function () { return String(projectVersion); },
|
||||
getScriptFileNames: function () { return __spread(memoryCache.files.keys()); },
|
||||
getScriptVersion: function (fileName) {
|
||||
var _a;
|
||||
var normalizedFileName = path_1.normalize(fileName);
|
||||
var version = memoryCache.files.get(normalizedFileName).version;
|
||||
var version = (_a = memoryCache.files.get(normalizedFileName)) === null || _a === void 0 ? void 0 : _a.version;
|
||||
return version === undefined ? undefined : String(version);
|
||||
},
|
||||
getScriptSnapshot: function (fileName) {
|
||||
var _a;
|
||||
var normalizedFileName = path_1.normalize(fileName);
|
||||
var hit = memoryCache.files.has(normalizedFileName) && memoryCache.files.get(normalizedFileName).version !== 0;
|
||||
var hit = isFileInCache(normalizedFileName);
|
||||
logger.trace({ normalizedFileName: normalizedFileName, cacheHit: hit }, 'getScriptSnapshot():', 'cache', hit ? 'hit' : 'miss');
|
||||
if (!hit) {
|
||||
memoryCache.files.set(normalizedFileName, {
|
||||
text: ts.sys.readFile(normalizedFileName),
|
||||
text: cacheReadFile(normalizedFileName),
|
||||
version: 1,
|
||||
});
|
||||
}
|
||||
@@ -102,46 +204,75 @@ exports.initializeLanguageServiceInstance = function (configs, memoryCache, logg
|
||||
return ts.ScriptSnapshot.fromString(contents);
|
||||
},
|
||||
fileExists: memoize(ts.sys.fileExists),
|
||||
readFile: logger.wrap(serviceHostTraceCtx, 'readFile', memoize(ts.sys.readFile)),
|
||||
readFile: cacheReadFile,
|
||||
readDirectory: memoize(ts.sys.readDirectory),
|
||||
getDirectories: memoize(ts.sys.getDirectories),
|
||||
directoryExists: memoize(ts.sys.directoryExists),
|
||||
realpath: memoize(ts.sys.realpath),
|
||||
realpath: ts.sys.realpath && memoize(ts.sys.realpath),
|
||||
getNewLine: function () { return constants_1.LINE_FEED; },
|
||||
getCurrentDirectory: function () { return cwd; },
|
||||
getCompilationSettings: function () { return options; },
|
||||
getDefaultLibFileName: function () { return ts.getDefaultLibFilePath(options); },
|
||||
getCustomTransformers: function () { return configs.tsCustomTransformers; },
|
||||
getCustomTransformers: function () { return configs.customTransformers; },
|
||||
resolveModuleNames: resolveModuleNames,
|
||||
};
|
||||
logger.debug('initializeLanguageServiceInstance(): creating language service');
|
||||
var service = ts.createLanguageService(serviceHost, ts.createDocumentRegistry());
|
||||
return {
|
||||
compileFn: function (code, fileName) {
|
||||
var e_1, _a;
|
||||
var _b;
|
||||
logger.debug({ fileName: fileName }, 'compileFn(): compiling using language service');
|
||||
updateMemoryCache(code, fileName);
|
||||
var output = service.getEmitOutput(fileName);
|
||||
logger.debug({ fileName: fileName }, 'compileFn(): computing diagnostics using language service');
|
||||
doTypeChecking(configs, fileName, service, logger);
|
||||
if (cacheDir) {
|
||||
if (compiler_utils_1.isTestFile(configs.testMatchPatterns, fileName)) {
|
||||
compiler_utils_1.cacheResolvedModules(fileName, code, memoryCache, service.getProgram(), cacheDir, logger);
|
||||
}
|
||||
else {
|
||||
Object.entries(memoryCache.resolvedModules)
|
||||
.filter(function (entry) {
|
||||
return entry[1].modulePaths.find(function (modulePath) { return modulePath === fileName; }) && !memoryCache.files.has(entry[0]);
|
||||
})
|
||||
.forEach(function (entry) {
|
||||
var testFileName = entry[0];
|
||||
var testFileContent = entry[1].testFileContent;
|
||||
logger.debug({ fileName: fileName }, 'compileFn(): computing diagnostics for test file that imports this module using language service');
|
||||
updateMemoryCache(testFileContent, testFileName);
|
||||
doTypeChecking(configs, testFileName, service, logger);
|
||||
if (tsResolvedModulesCachePath) {
|
||||
void (function () { return __awaiter(void 0, void 0, void 0, function () {
|
||||
return __generator(this, function (_a) {
|
||||
switch (_a.label) {
|
||||
case 0: return [4, fs_1.writeFile(tsResolvedModulesCachePath, json_1.stringify(__spread(memoryCache.resolvedModules)), function () { })];
|
||||
case 1:
|
||||
_a.sent();
|
||||
return [2];
|
||||
}
|
||||
});
|
||||
}); })();
|
||||
}
|
||||
if (!diagnosedFiles.includes(fileName)) {
|
||||
logger.debug({ fileName: fileName }, 'compileFn(): computing diagnostics using language service');
|
||||
doTypeChecking(configs, diagnosedFiles, fileName, service, logger);
|
||||
}
|
||||
if (!configs.isTestFile(fileName)) {
|
||||
try {
|
||||
for (var _c = __values(memoryCache.resolvedModules.entries()), _d = _c.next(); !_d.done; _d = _c.next()) {
|
||||
var _e = __read(_d.value, 2), testFileName = _e[0], resolvedModules = _e[1];
|
||||
if (resolvedModules.includes(fileName) &&
|
||||
!diagnosedFiles.includes(testFileName) &&
|
||||
fs_1.existsSync(testFileName)) {
|
||||
var testFileContent = (_b = memoryCache.files.get(testFileName)) === null || _b === void 0 ? void 0 : _b.text;
|
||||
if (!testFileContent) {
|
||||
updateMemoryCache(cacheReadFile(testFileName), testFileName);
|
||||
}
|
||||
logger.debug({ testFileName: testFileName }, 'compileFn(): computing diagnostics using language service for test file which uses the module');
|
||||
doTypeChecking(configs, diagnosedFiles, testFileName, service, logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
}
|
||||
if (output.emitSkipped) {
|
||||
throw new TypeError(path_1.relative(cwd, fileName) + ": Emit skipped for language service");
|
||||
if (constants_1.TS_TSX_REGEX.test(fileName)) {
|
||||
throw new Error(messages_1.interpolate("Unable to process '{{file}}', please make sure that `outDir` in your tsconfig is neither `''` or `'.'`. You can also configure Jest config option `transformIgnorePatterns` to inform `ts-jest` to transform {{file}}", { file: fileName }));
|
||||
}
|
||||
else {
|
||||
logger.warn(messages_1.interpolate("Unable to process '{{file}}', falling back to original file content. You can also configure Jest config option `transformIgnorePatterns` to ignore {{file}} from transformation or make sure that `outDir` in your tsconfig is neither `''` or `'.'`", { file: fileName }));
|
||||
return [code, '{}'];
|
||||
}
|
||||
}
|
||||
if (!output.outputFiles.length) {
|
||||
throw new TypeError(messages_1.interpolate("Unable to require `.d.ts` file for file: {{file}}.\nThis is usually the result of a faulty configuration or import. Make sure there is a `.js`, `.json` or another executable extension available alongside `{{file}}`.", {
|
||||
|
||||
+7
-15
@@ -1,32 +1,24 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.initializeTranspilerInstance = function (configs, memoryCache, logger) {
|
||||
exports.initializeTranspilerInstance = void 0;
|
||||
exports.initializeTranspilerInstance = function (configs, logger) {
|
||||
logger.debug('initializeTranspilerInstance(): create typescript compiler');
|
||||
var _a = configs.parsedTsConfig, options = _a.options, fileNames = _a.fileNames;
|
||||
var options = configs.parsedTsConfig.options;
|
||||
var ts = configs.compilerModule;
|
||||
var program = ts.createProgram(fileNames, options);
|
||||
var updateFileInCache = function (contents, filePath) {
|
||||
var file = memoryCache.files.get(filePath);
|
||||
if (file && file.text !== contents) {
|
||||
file.version++;
|
||||
file.text = contents;
|
||||
}
|
||||
};
|
||||
return {
|
||||
compileFn: function (code, fileName) {
|
||||
updateFileInCache(code, fileName);
|
||||
logger.debug({ fileName: fileName }, 'compileFn(): compiling as isolated module');
|
||||
var result = ts.transpileModule(code, {
|
||||
fileName: fileName,
|
||||
transformers: configs.tsCustomTransformers,
|
||||
transformers: configs.customTransformers,
|
||||
compilerOptions: options,
|
||||
reportDiagnostics: configs.shouldReportDiagnostic(fileName),
|
||||
reportDiagnostics: configs.shouldReportDiagnostics(fileName),
|
||||
});
|
||||
if (result.diagnostics && configs.shouldReportDiagnostic(fileName)) {
|
||||
if (result.diagnostics && configs.shouldReportDiagnostics(fileName)) {
|
||||
configs.raiseDiagnostics(result.diagnostics, fileName, logger);
|
||||
}
|
||||
return [result.outputText, result.sourceMapText];
|
||||
},
|
||||
program: program,
|
||||
program: undefined,
|
||||
};
|
||||
};
|
||||
|
||||
+24
-7
@@ -1,11 +1,28 @@
|
||||
import { Config } from '@jest/types';
|
||||
import type { Config } from '@jest/types';
|
||||
import { Logger } from 'bs-logger';
|
||||
import { TsCompiler, TsJestGlobalOptions } from '../types';
|
||||
import { CompilerOptions, CustomTransformers, Diagnostic, ParsedCommandLine } from 'typescript';
|
||||
import type { TTypeScript } from '../types';
|
||||
import type { RawCompilerOptions } from '../tsconfig-raw';
|
||||
export declare class ConfigSet {
|
||||
readonly parentOptions?: TsJestGlobalOptions | undefined;
|
||||
get versions(): Record<string, string>;
|
||||
get tsCompiler(): TsCompiler;
|
||||
get tsJestDigest(): string;
|
||||
private readonly parentLogger?;
|
||||
readonly logger: Logger;
|
||||
constructor(jestConfig: Config.ProjectConfig, parentOptions?: TsJestGlobalOptions | undefined, parentLogger?: Logger);
|
||||
readonly compilerModule: TTypeScript;
|
||||
readonly isolatedModules: boolean;
|
||||
readonly cwd: string;
|
||||
tsCacheDir: string | undefined;
|
||||
parsedTsConfig: ParsedCommandLine | Record<string, any>;
|
||||
customTransformers: CustomTransformers;
|
||||
readonly rootDir: string;
|
||||
protected _overriddenCompilerOptions: Partial<CompilerOptions>;
|
||||
constructor(jestConfig: Config.ProjectConfig | undefined, parentLogger?: Logger | undefined);
|
||||
protected _resolveTsConfig(compilerOptions?: RawCompilerOptions, resolvedConfigFile?: string): Record<string, any>;
|
||||
get tsJestDigest(): string;
|
||||
isTestFile(fileName: string): boolean;
|
||||
shouldStringifyContent(filePath: string): boolean;
|
||||
raiseDiagnostics(diagnostics: Diagnostic[], filePath?: string, logger?: Logger): void;
|
||||
shouldReportDiagnostics(filePath: string): boolean;
|
||||
resolvePath(inputPath: string, { throwIfMissing, nodeResolve }?: {
|
||||
throwIfMissing?: boolean;
|
||||
nodeResolve?: boolean;
|
||||
}): string;
|
||||
}
|
||||
|
||||
+348
-598
File diff suppressed because it is too large
Load Diff
-10
@@ -1,10 +0,0 @@
|
||||
import { Config } from '@jest/types';
|
||||
export interface TsJestPresets {
|
||||
transform: Config.InitialOptions['transform'];
|
||||
testMatch?: string[];
|
||||
moduleFileExtensions?: string[];
|
||||
}
|
||||
export interface CreateJestPresetOptions {
|
||||
allowJs?: boolean;
|
||||
}
|
||||
export declare function createJestPreset({ allowJs }?: CreateJestPresetOptions, from?: Config.InitialOptions): TsJestPresets;
|
||||
+5
-4
@@ -1,5 +1,6 @@
|
||||
import type { Config } from '@jest/types';
|
||||
declare type JestPathMapping = Config.InitialOptions['moduleNameMapper'];
|
||||
export declare const pathsToModuleNameMapper: (mapping: import("typescript").MapLike<string[]>, { prefix }?: {
|
||||
prefix?: string | undefined;
|
||||
}) => {
|
||||
[key: string]: string | string[];
|
||||
} | undefined;
|
||||
prefix: string;
|
||||
}) => JestPathMapping;
|
||||
export {};
|
||||
|
||||
+14
-13
@@ -12,14 +12,15 @@ var __values = (this && this.__values) || function(o) {
|
||||
};
|
||||
var _a;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.pathsToModuleNameMapper = void 0;
|
||||
var bs_logger_1 = require("bs-logger");
|
||||
var logger_1 = require("../util/logger");
|
||||
var messages_1 = require("../util/messages");
|
||||
var logger_1 = require("../utils/logger");
|
||||
var messages_1 = require("../utils/messages");
|
||||
var escapeRegex = function (str) { return str.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&'); };
|
||||
var logger = logger_1.rootLogger.child((_a = {}, _a[bs_logger_1.LogContexts.namespace] = 'path-mapper', _a));
|
||||
exports.pathsToModuleNameMapper = function (mapping, _a) {
|
||||
var e_1, _b;
|
||||
var _c = (_a === void 0 ? {} : _a).prefix, prefix = _c === void 0 ? '' : _c;
|
||||
var _c = (_a === void 0 ? Object.create(null) : _a).prefix, prefix = _c === void 0 ? '' : _c;
|
||||
var jestMap = {};
|
||||
try {
|
||||
for (var _d = __values(Object.keys(mapping)), _e = _d.next(); !_e.done; _e = _d.next()) {
|
||||
@@ -30,25 +31,25 @@ exports.pathsToModuleNameMapper = function (mapping, _a) {
|
||||
logger.warn(messages_1.interpolate("Not mapping \"{{path}}\" because it has no target.", { path: fromPath }));
|
||||
continue;
|
||||
}
|
||||
else if (toPaths.length > 1) {
|
||||
logger.warn(messages_1.interpolate("Mapping only to first target of \"{{path}}\" because it has more than one ({{count}}).", {
|
||||
path: fromPath,
|
||||
count: toPaths.length,
|
||||
}));
|
||||
}
|
||||
var target = toPaths[0];
|
||||
var segments = fromPath.split(/\*/g);
|
||||
if (segments.length === 1) {
|
||||
var paths = toPaths.map(function (target) {
|
||||
var enrichedPrefix = prefix !== '' && !prefix.endsWith('/') ? prefix + "/" : prefix;
|
||||
return "" + enrichedPrefix + target;
|
||||
});
|
||||
pattern = "^" + escapeRegex(fromPath) + "$";
|
||||
jestMap[pattern] = "" + prefix + target;
|
||||
jestMap[pattern] = paths.length === 1 ? paths[0] : paths;
|
||||
}
|
||||
else if (segments.length === 2) {
|
||||
var paths = toPaths.map(function (target) {
|
||||
var enrichedPrefix = prefix !== '' && !prefix.endsWith('/') ? prefix + "/" : prefix;
|
||||
return "" + enrichedPrefix + target.replace(/\*/g, '$1');
|
||||
});
|
||||
pattern = "^" + escapeRegex(segments[0]) + "(.*)" + escapeRegex(segments[1]) + "$";
|
||||
jestMap[pattern] = "" + prefix + target.replace(/\*/g, '$1');
|
||||
jestMap[pattern] = paths.length === 1 ? paths[0] : paths;
|
||||
}
|
||||
else {
|
||||
logger.warn(messages_1.interpolate("Not mapping \"{{path}}\" because it has more than one star (`*`).", { path: fromPath }));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -1 +1,4 @@
|
||||
export {};
|
||||
export declare const LINE_FEED = "\n";
|
||||
export declare const TS_TSX_REGEX: RegExp;
|
||||
export declare const JS_JSX_REGEX: RegExp;
|
||||
export declare const DECLARATION_TYPE_EXT = ".d.ts";
|
||||
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.DEFAULT_JEST_TEST_MATCH = exports.DECLARATION_TYPE_EXT = exports.JS_JSX_REGEX = exports.TS_TSX_REGEX = exports.LINE_FEED = void 0;
|
||||
exports.LINE_FEED = '\n';
|
||||
exports.EXTENSION_REGEX = /\.[^.]+$/;
|
||||
exports.TS_TSX_REGEX = /\.tsx?$/;
|
||||
exports.JS_JSX_REGEX = /\.jsx?$/;
|
||||
exports.JSON_REGEX = /\.json$/i;
|
||||
exports.DECLARATION_TYPE_EXT = '.d.ts';
|
||||
exports.DEFAULT_JEST_TEST_MATCH = ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)'];
|
||||
|
||||
+5
-9
@@ -1,7 +1,7 @@
|
||||
import { createJestPreset as createJestPresetCore } from './config/create-jest-preset';
|
||||
import { createJestPreset as createJestPresetCore } from './presets/create-jest-preset';
|
||||
import { TsJestTransformer } from './ts-jest-transformer';
|
||||
import { TsJestGlobalOptions } from './types';
|
||||
import { mocked as mockedCore } from './util/testing';
|
||||
import type { TsJestGlobalOptions } from './types';
|
||||
import { mocked as mockedCore } from './utils/testing';
|
||||
declare module '@jest/types' {
|
||||
namespace Config {
|
||||
interface ConfigGlobals {
|
||||
@@ -12,12 +12,8 @@ declare module '@jest/types' {
|
||||
export declare const mocked: typeof mockedCore;
|
||||
export declare const createJestPreset: typeof createJestPresetCore;
|
||||
export declare const pathsToModuleNameMapper: (mapping: import("typescript").MapLike<string[]>, { prefix }?: {
|
||||
prefix?: string | undefined;
|
||||
prefix: string;
|
||||
}) => {
|
||||
[key: string]: string | string[];
|
||||
} | undefined;
|
||||
export declare const version: string;
|
||||
export declare const digest: string;
|
||||
export declare function createTransformer(baseConfig?: TsJestGlobalOptions): TsJestTransformer;
|
||||
declare const jestPreset: import("./config/create-jest-preset").TsJestPresets;
|
||||
export { jestPreset, };
|
||||
export declare function createTransformer(): TsJestTransformer;
|
||||
|
||||
+8
-58
@@ -1,36 +1,15 @@
|
||||
"use strict";
|
||||
var __read = (this && this.__read) || function (o, n) {
|
||||
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
||||
if (!m) return o;
|
||||
var i = m.call(o), r, ar = [], e;
|
||||
try {
|
||||
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
||||
}
|
||||
catch (error) { e = { error: error }; }
|
||||
finally {
|
||||
try {
|
||||
if (r && !r.done && (m = i["return"])) m.call(i);
|
||||
}
|
||||
finally { if (e) throw e.error; }
|
||||
}
|
||||
return ar;
|
||||
};
|
||||
var __spread = (this && this.__spread) || function () {
|
||||
for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i]));
|
||||
return ar;
|
||||
};
|
||||
var _a;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createTransformer = exports.pathsToModuleNameMapper = exports.createJestPreset = exports.mocked = void 0;
|
||||
var bs_logger_1 = require("bs-logger");
|
||||
var fs_1 = require("fs");
|
||||
var path_1 = require("path");
|
||||
var create_jest_preset_1 = require("./config/create-jest-preset");
|
||||
var create_jest_preset_1 = require("./presets/create-jest-preset");
|
||||
var paths_to_module_name_mapper_1 = require("./config/paths-to-module-name-mapper");
|
||||
var ts_jest_transformer_1 = require("./ts-jest-transformer");
|
||||
var logger_1 = require("./util/logger");
|
||||
var messages_1 = require("./util/messages");
|
||||
var testing_1 = require("./util/testing");
|
||||
var version_checkers_1 = require("./util/version-checkers");
|
||||
var logger_1 = require("./utils/logger");
|
||||
var messages_1 = require("./utils/messages");
|
||||
var testing_1 = require("./utils/testing");
|
||||
var version_checkers_1 = require("./utils/version-checkers");
|
||||
var warn = logger_1.rootLogger.child((_a = {}, _a[bs_logger_1.LogContexts.logLevel] = bs_logger_1.LogLevels.warn, _a));
|
||||
var helperMoved = function (name, helper) {
|
||||
return warn.wrap(messages_1.interpolate("The `{{helper}}` helper has been moved to `ts-jest/utils`. Use `import { {{helper}} } from 'ts-jest/utils'` instead.", { helper: name }), helper);
|
||||
@@ -38,37 +17,8 @@ var helperMoved = function (name, helper) {
|
||||
exports.mocked = helperMoved('mocked', testing_1.mocked);
|
||||
exports.createJestPreset = helperMoved('createJestPreset', create_jest_preset_1.createJestPreset);
|
||||
exports.pathsToModuleNameMapper = helperMoved('pathsToModuleNameMapper', paths_to_module_name_mapper_1.pathsToModuleNameMapper);
|
||||
exports.version = require('../package.json').version;
|
||||
exports.digest = fs_1.readFileSync(path_1.resolve(__dirname, '..', '.ts-jest-digest'), 'utf8');
|
||||
var transformer;
|
||||
function defaultTransformer() {
|
||||
return transformer || (transformer = createTransformer());
|
||||
}
|
||||
function createTransformer(baseConfig) {
|
||||
function createTransformer() {
|
||||
version_checkers_1.VersionCheckers.jest.warn();
|
||||
return new ts_jest_transformer_1.TsJestTransformer(baseConfig);
|
||||
return new ts_jest_transformer_1.TsJestTransformer();
|
||||
}
|
||||
exports.createTransformer = createTransformer;
|
||||
function process() {
|
||||
var _a;
|
||||
var args = [];
|
||||
for (var _i = 0; _i < arguments.length; _i++) {
|
||||
args[_i] = arguments[_i];
|
||||
}
|
||||
return (_a = defaultTransformer()).process.apply(_a, __spread(args));
|
||||
}
|
||||
exports.process = process;
|
||||
function getCacheKey() {
|
||||
var _a;
|
||||
var args = [];
|
||||
for (var _i = 0; _i < arguments.length; _i++) {
|
||||
args[_i] = arguments[_i];
|
||||
}
|
||||
return (_a = defaultTransformer()).getCacheKey.apply(_a, __spread(args));
|
||||
}
|
||||
exports.getCacheKey = getCacheKey;
|
||||
exports.canInstrument = false;
|
||||
var jestPreset = create_jest_preset_1.createJestPreset();
|
||||
exports.jestPreset = jestPreset;
|
||||
exports.__singleton = function () { return transformer; };
|
||||
exports.__resetModule = function () { return (transformer = undefined); };
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import type { Config } from '@jest/types';
|
||||
import type { TsJestPresets } from '../types';
|
||||
interface CreateJestPresetOptions {
|
||||
allowJs?: boolean;
|
||||
}
|
||||
export declare function createJestPreset({ allowJs }?: CreateJestPresetOptions, from?: Config.InitialOptions): TsJestPresets;
|
||||
export {};
|
||||
Generated
Vendored
+2
-1
@@ -11,7 +11,8 @@ var __assign = (this && this.__assign) || function () {
|
||||
return __assign.apply(this, arguments);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var logger_1 = require("../util/logger");
|
||||
exports.createJestPreset = void 0;
|
||||
var logger_1 = require("../utils/logger");
|
||||
var logger = logger_1.rootLogger.child({ namespace: 'jest-preset' });
|
||||
function createJestPreset(_a, from) {
|
||||
var _b;
|
||||
+60
-10
@@ -1,4 +1,15 @@
|
||||
"use strict";
|
||||
var __assign = (this && this.__assign) || function () {
|
||||
__assign = Object.assign || function(t) {
|
||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||
s = arguments[i];
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||
t[p] = s[p];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
return __assign.apply(this, arguments);
|
||||
};
|
||||
var __read = (this && this.__read) || function (o, n) {
|
||||
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
||||
if (!m) return o;
|
||||
@@ -20,23 +31,44 @@ var __spread = (this && this.__spread) || function () {
|
||||
return ar;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.factory = exports.version = exports.name = void 0;
|
||||
var bs_logger_1 = require("bs-logger");
|
||||
var HOIST_METHODS = ['mock', 'unmock', 'enableAutomock', 'disableAutomock', 'deepUnmock'];
|
||||
var JEST_GLOBALS_MODULE_NAME = '@jest/globals';
|
||||
var JEST_GLOBAL_NAME = 'jest';
|
||||
var ROOT_LEVEL_AST = 1;
|
||||
exports.name = 'hoisting-jest-mock';
|
||||
exports.version = 1;
|
||||
exports.version = 4;
|
||||
function factory(cs) {
|
||||
var logger = cs.logger.child({ namespace: 'ts-hoisting' });
|
||||
var ts = cs.compilerModule;
|
||||
function shouldHoistExpression(expression) {
|
||||
return (ts.isCallExpression(expression) &&
|
||||
ts.isPropertyAccessExpression(expression.expression) &&
|
||||
HOIST_METHODS.includes(expression.expression.name.text) &&
|
||||
((ts.isIdentifier(expression.expression.expression) && expression.expression.expression.text === 'jest') ||
|
||||
shouldHoistExpression(expression.expression.expression)));
|
||||
var importNames = [];
|
||||
function shouldHoistExpression(node) {
|
||||
if (ts.isCallExpression(node) &&
|
||||
ts.isPropertyAccessExpression(node.expression) &&
|
||||
HOIST_METHODS.includes(node.expression.name.text)) {
|
||||
if (importNames.length) {
|
||||
return ((ts.isIdentifier(node.expression.expression) && importNames.includes(node.expression.expression.text)) ||
|
||||
(ts.isPropertyAccessExpression(node.expression.expression) &&
|
||||
ts.isIdentifier(node.expression.expression.expression) &&
|
||||
importNames.includes(node.expression.expression.expression.text)) ||
|
||||
shouldHoistExpression(node.expression.expression));
|
||||
}
|
||||
else {
|
||||
return ((ts.isIdentifier(node.expression.expression) && node.expression.expression.text === JEST_GLOBAL_NAME) ||
|
||||
shouldHoistExpression(node.expression.expression));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function shouldHoistNode(node) {
|
||||
return ts.isExpressionStatement(node) && shouldHoistExpression(node.expression);
|
||||
}
|
||||
function isJestGlobalImport(node) {
|
||||
return (ts.isImportDeclaration(node) &&
|
||||
ts.isStringLiteral(node.moduleSpecifier) &&
|
||||
node.moduleSpecifier.text === JEST_GLOBALS_MODULE_NAME);
|
||||
}
|
||||
function createVisitor(ctx, _) {
|
||||
var level = 0;
|
||||
var hoisted = [];
|
||||
@@ -56,14 +88,32 @@ function factory(cs) {
|
||||
}
|
||||
};
|
||||
var visitor = function (node) {
|
||||
var _a, _b;
|
||||
enter();
|
||||
var resultNode = ts.visitEachChild(node, visitor, ctx);
|
||||
if (isJestGlobalImport(resultNode) && ((_a = resultNode.importClause) === null || _a === void 0 ? void 0 : _a.namedBindings) &&
|
||||
(ts.isNamespaceImport(resultNode.importClause.namedBindings) ||
|
||||
ts.isNamedImports(resultNode.importClause.namedBindings))) {
|
||||
var namedBindings = resultNode.importClause.namedBindings;
|
||||
var jestImportName = ts.isNamespaceImport(namedBindings)
|
||||
? namedBindings.name.text
|
||||
: (_b = namedBindings.elements.find(function (element) { var _a; return element.name.text === JEST_GLOBAL_NAME || ((_a = element.propertyName) === null || _a === void 0 ? void 0 : _a.text) === JEST_GLOBAL_NAME; })) === null || _b === void 0 ? void 0 : _b.name.text;
|
||||
if (jestImportName) {
|
||||
importNames.push(jestImportName);
|
||||
}
|
||||
}
|
||||
if (hoisted[level] && hoisted[level].length) {
|
||||
var hoistedStmts_1 = hoisted[level];
|
||||
var otherStmts = resultNode.statements.filter(function (s) { return !hoistedStmts_1.includes(s); });
|
||||
var otherStmts = resultNode.statements.filter(function (s) { return !hoistedStmts_1.includes(s) && !isJestGlobalImport(s); });
|
||||
var newNode = ts.getMutableClone(resultNode);
|
||||
newNode.statements = ts.createNodeArray(__spread(hoistedStmts_1, otherStmts));
|
||||
resultNode = newNode;
|
||||
var newStatements = __spread(hoistedStmts_1, otherStmts);
|
||||
if (level === ROOT_LEVEL_AST) {
|
||||
var jestGlobalsImportStmts = resultNode.statements.filter(function (s) { return isJestGlobalImport(s); });
|
||||
resultNode = __assign(__assign({}, newNode), { statements: ts.createNodeArray(__spread(jestGlobalsImportStmts, newStatements)) });
|
||||
}
|
||||
else {
|
||||
resultNode = __assign(__assign({}, newNode), { statements: ts.createNodeArray(newStatements) });
|
||||
}
|
||||
}
|
||||
exit();
|
||||
if (shouldHoistNode(resultNode)) {
|
||||
|
||||
+21
-1
@@ -1,4 +1,24 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var hoisting = require("./hoist-jest");
|
||||
exports.internals = void 0;
|
||||
var hoisting = __importStar(require("./hoist-jest"));
|
||||
exports.internals = [hoisting];
|
||||
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
import type * as _ts from 'typescript';
|
||||
import type { ConfigSet } from '../config/config-set';
|
||||
export declare function factory(cs: ConfigSet): (ctx: _ts.TransformationContext) => _ts.Transformer<_ts.SourceFile>;
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
"use strict";
|
||||
var __assign = (this && this.__assign) || function () {
|
||||
__assign = Object.assign || function(t) {
|
||||
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
||||
s = arguments[i];
|
||||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
||||
t[p] = s[p];
|
||||
}
|
||||
return t;
|
||||
};
|
||||
return __assign.apply(this, arguments);
|
||||
};
|
||||
var __values = (this && this.__values) || function(o) {
|
||||
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
||||
if (m) return m.call(o);
|
||||
if (o && typeof o.length === "number") return {
|
||||
next: function () {
|
||||
if (o && i >= o.length) o = void 0;
|
||||
return { value: o && o[i++], done: !o };
|
||||
}
|
||||
};
|
||||
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.factory = exports.version = exports.name = void 0;
|
||||
var bs_logger_1 = require("bs-logger");
|
||||
var path_1 = require("path");
|
||||
exports.name = 'path-mapping';
|
||||
exports.version = 1;
|
||||
var isBaseDir = function (base, dir) { var _a; return !((_a = path_1.relative(base, dir)) === null || _a === void 0 ? void 0 : _a.startsWith('.')); };
|
||||
function factory(cs) {
|
||||
var _a;
|
||||
var logger = cs.logger.child({ namespace: 'ts-path-mapping' });
|
||||
var ts = cs.compilerModule;
|
||||
var compilerOptions = cs.parsedTsConfig.options;
|
||||
var rootDirs = (_a = compilerOptions.rootDirs) === null || _a === void 0 ? void 0 : _a.filter(path_1.isAbsolute);
|
||||
var isDynamicImport = function (node) {
|
||||
return ts.isCallExpression(node) && node.expression.kind === ts.SyntaxKind.ImportKeyword;
|
||||
};
|
||||
var isRequire = function (node) {
|
||||
return ts.isCallExpression(node) &&
|
||||
ts.isIdentifier(node.expression) &&
|
||||
node.expression.text === 'require' &&
|
||||
ts.isStringLiteral(node.arguments[0]) &&
|
||||
node.arguments.length === 1;
|
||||
};
|
||||
var createVisitor = function (ctx, sf) {
|
||||
var fileName = sf.fileName;
|
||||
var fileDir = path_1.normalize(path_1.dirname(fileName));
|
||||
var rewritePath = function (importPath) {
|
||||
var e_1, _a;
|
||||
var p = importPath;
|
||||
var resolvedModule = ts.resolveModuleName(importPath, fileName, compilerOptions, ts.sys).resolvedModule;
|
||||
if (resolvedModule) {
|
||||
var resolvedFileName = resolvedModule.resolvedFileName;
|
||||
var filePath = fileDir;
|
||||
var modulePath = path_1.dirname(resolvedFileName);
|
||||
if (rootDirs) {
|
||||
var fileRootDir = '';
|
||||
var moduleRootDir = '';
|
||||
try {
|
||||
for (var rootDirs_1 = __values(rootDirs), rootDirs_1_1 = rootDirs_1.next(); !rootDirs_1_1.done; rootDirs_1_1 = rootDirs_1.next()) {
|
||||
var rootDir = rootDirs_1_1.value;
|
||||
if (isBaseDir(rootDir, resolvedFileName) && rootDir.length > moduleRootDir.length)
|
||||
moduleRootDir = rootDir;
|
||||
if (isBaseDir(rootDir, fileName) && rootDir.length > fileRootDir.length)
|
||||
fileRootDir = rootDir;
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (rootDirs_1_1 && !rootDirs_1_1.done && (_a = rootDirs_1.return)) _a.call(rootDirs_1);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
if (fileRootDir && moduleRootDir) {
|
||||
filePath = path_1.relative(fileRootDir, filePath);
|
||||
modulePath = path_1.relative(moduleRootDir, modulePath);
|
||||
}
|
||||
}
|
||||
p = path_1.normalize(path_1.join(path_1.relative(filePath, modulePath), path_1.basename(resolvedFileName)));
|
||||
p = p.startsWith('.') ? p : "./" + p;
|
||||
}
|
||||
return p;
|
||||
};
|
||||
var visitor = function (node) {
|
||||
var rewrittenPath;
|
||||
var newNode = ts.getMutableClone(node);
|
||||
if (isDynamicImport(node) || isRequire(node)) {
|
||||
rewrittenPath = rewritePath(node.arguments[0].text);
|
||||
return __assign(__assign({}, newNode), { arguments: ts.createNodeArray([ts.createStringLiteral(rewrittenPath)]) });
|
||||
}
|
||||
if (ts.isExternalModuleReference(node) && ts.isStringLiteral(node.expression)) {
|
||||
rewrittenPath = rewritePath(node.expression.text);
|
||||
return ts.updateExternalModuleReference(newNode, ts.createLiteral(rewrittenPath));
|
||||
}
|
||||
if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
|
||||
rewrittenPath = rewritePath(node.moduleSpecifier.text);
|
||||
return __assign(__assign({}, newNode), { moduleSpecifier: ts.createLiteral(rewrittenPath) });
|
||||
}
|
||||
if (ts.isExportDeclaration(node) && node.moduleSpecifier && ts.isStringLiteral(node.moduleSpecifier)) {
|
||||
rewrittenPath = rewritePath(node.moduleSpecifier.text);
|
||||
return __assign(__assign({}, newNode), { moduleSpecifier: ts.createLiteral(rewrittenPath) });
|
||||
}
|
||||
if (ts.isImportTypeNode(node) &&
|
||||
ts.isLiteralTypeNode(node.argument) &&
|
||||
ts.isStringLiteral(node.argument.literal)) {
|
||||
rewrittenPath = rewritePath(node.argument.literal.text);
|
||||
return __assign(__assign({}, newNode), { argument: ts.createLiteralTypeNode(ts.createStringLiteral(rewrittenPath)) });
|
||||
}
|
||||
return ts.visitEachChild(node, visitor, ctx);
|
||||
};
|
||||
return visitor;
|
||||
};
|
||||
return function (ctx) {
|
||||
var _a;
|
||||
return logger.wrap((_a = {}, _a[bs_logger_1.LogContexts.logLevel] = bs_logger_1.LogLevels.debug, _a.call = null, _a), 'visitSourceFileNode(): path mapping', function (sf) { return ts.visitNode(sf, createVisitor(ctx, sf)); });
|
||||
};
|
||||
}
|
||||
exports.factory = factory;
|
||||
+6
-7
@@ -1,12 +1,11 @@
|
||||
import { CacheKeyOptions, TransformOptions, TransformedSource, Transformer } from '@jest/transform';
|
||||
import { Config } from '@jest/types';
|
||||
import type { CacheKeyOptions, TransformedSource, Transformer, TransformOptions } from '@jest/transform';
|
||||
import type { Config } from '@jest/types';
|
||||
import type { Logger } from 'bs-logger';
|
||||
import { ConfigSet } from './config/config-set';
|
||||
import { TsJestGlobalOptions } from './types';
|
||||
export declare class TsJestTransformer implements Transformer {
|
||||
private readonly logger;
|
||||
private readonly id;
|
||||
private readonly options;
|
||||
constructor(baseOptions?: TsJestGlobalOptions);
|
||||
protected readonly logger: Logger;
|
||||
protected _transformCfgStr: string;
|
||||
constructor();
|
||||
configsFor(jestConfig: Config.ProjectConfig): ConfigSet;
|
||||
process(input: string, filePath: Config.Path, jestConfig: Config.ProjectConfig, transformOptions?: TransformOptions): TransformedSource | string;
|
||||
getCacheKey(fileContent: string, filePath: string, _jestConfigStr: string, transformOptions: CacheKeyOptions): string;
|
||||
|
||||
+49
-50
@@ -11,53 +11,53 @@ var __assign = (this && this.__assign) || function () {
|
||||
return __assign.apply(this, arguments);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
var util_1 = require("util");
|
||||
exports.TsJestTransformer = void 0;
|
||||
var config_set_1 = require("./config/config-set");
|
||||
var constants_1 = require("./constants");
|
||||
var json_1 = require("./util/json");
|
||||
var jsonable_value_1 = require("./util/jsonable-value");
|
||||
var logger_1 = require("./util/logger");
|
||||
var messages_1 = require("./util/messages");
|
||||
var sha1_1 = require("./util/sha1");
|
||||
var INSPECT_CUSTOM = util_1.inspect.custom || 'inspect';
|
||||
var json_1 = require("./utils/json");
|
||||
var jsonable_value_1 = require("./utils/jsonable-value");
|
||||
var logger_1 = require("./utils/logger");
|
||||
var messages_1 = require("./utils/messages");
|
||||
var sha1_1 = require("./utils/sha1");
|
||||
var TsJestTransformer = (function () {
|
||||
function TsJestTransformer(baseOptions) {
|
||||
if (baseOptions === void 0) { baseOptions = {}; }
|
||||
this.options = __assign({}, baseOptions);
|
||||
this.id = TsJestTransformer._nextTransformerId;
|
||||
this.logger = logger_1.rootLogger.child({
|
||||
transformerId: this.id,
|
||||
namespace: 'jest-transformer',
|
||||
});
|
||||
this.logger.debug({ baseOptions: baseOptions }, 'created new transformer');
|
||||
function TsJestTransformer() {
|
||||
this.logger = logger_1.rootLogger.child({ namespace: 'ts-jest-transformer' });
|
||||
this.logger.debug('created new transformer');
|
||||
}
|
||||
Object.defineProperty(TsJestTransformer, "_nextTransformerId", {
|
||||
get: function () {
|
||||
return ++TsJestTransformer._lastTransformerId;
|
||||
},
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
TsJestTransformer.prototype[INSPECT_CUSTOM] = function () {
|
||||
return "[object TsJestTransformer<#" + this.id + ">]";
|
||||
};
|
||||
TsJestTransformer.prototype.configsFor = function (jestConfig) {
|
||||
var csi = TsJestTransformer._configSetsIndex.find(function (cs) { return cs.jestConfig.value === jestConfig; });
|
||||
if (csi)
|
||||
return csi.configSet;
|
||||
var serialized = json_1.stringify(jestConfig);
|
||||
csi = TsJestTransformer._configSetsIndex.find(function (cs) { return cs.jestConfig.serialized === serialized; });
|
||||
if (csi) {
|
||||
csi.jestConfig.value = jestConfig;
|
||||
return csi.configSet;
|
||||
var ccs = TsJestTransformer._cachedConfigSets.find(function (cs) { return cs.jestConfig.value === jestConfig; });
|
||||
var configSet;
|
||||
if (ccs) {
|
||||
this._transformCfgStr = ccs.transformerCfgStr;
|
||||
configSet = ccs.configSet;
|
||||
}
|
||||
else {
|
||||
var serializedJestCfg_1 = json_1.stringify(jestConfig);
|
||||
var serializedCcs = TsJestTransformer._cachedConfigSets.find(function (cs) { return cs.jestConfig.serialized === serializedJestCfg_1; });
|
||||
if (serializedCcs) {
|
||||
serializedCcs.jestConfig.value = jestConfig;
|
||||
this._transformCfgStr = serializedCcs.transformerCfgStr;
|
||||
configSet = serializedCcs.configSet;
|
||||
}
|
||||
else {
|
||||
this.logger.info('no matching config-set found, creating a new one');
|
||||
configSet = new config_set_1.ConfigSet(jestConfig);
|
||||
var jest_1 = __assign({}, jestConfig);
|
||||
var globals = (jest_1.globals = __assign({}, jest_1.globals));
|
||||
jest_1.name = undefined;
|
||||
jest_1.cacheDirectory = undefined;
|
||||
delete globals['ts-jest'];
|
||||
this._transformCfgStr = new jsonable_value_1.JsonableValue(__assign(__assign({ digest: configSet.tsJestDigest, babel: configSet.babelConfig }, jest_1), { tsconfig: {
|
||||
options: configSet.parsedTsConfig.options,
|
||||
raw: configSet.parsedTsConfig.raw,
|
||||
} })).serialized;
|
||||
TsJestTransformer._cachedConfigSets.push({
|
||||
jestConfig: new jsonable_value_1.JsonableValue(jestConfig),
|
||||
configSet: configSet,
|
||||
transformerCfgStr: this._transformCfgStr,
|
||||
});
|
||||
}
|
||||
}
|
||||
var jestConfigObj = jestConfig;
|
||||
this.logger.info('no matching config-set found, creating a new one');
|
||||
var configSet = new config_set_1.ConfigSet(jestConfigObj, this.options, this.logger);
|
||||
TsJestTransformer._configSetsIndex.push({
|
||||
jestConfig: new jsonable_value_1.JsonableValue(jestConfigObj),
|
||||
configSet: configSet,
|
||||
});
|
||||
return configSet;
|
||||
};
|
||||
TsJestTransformer.prototype.process = function (input, filePath, jestConfig, transformOptions) {
|
||||
@@ -66,13 +66,13 @@ var TsJestTransformer = (function () {
|
||||
var source = input;
|
||||
var configs = this.configsFor(jestConfig);
|
||||
var hooks = configs.hooks;
|
||||
var stringify = configs.shouldStringifyContent(filePath);
|
||||
var babelJest = stringify ? undefined : configs.babelJestTransformer;
|
||||
var isDefinitionFile = filePath.endsWith('.d.ts');
|
||||
var shouldStringifyContent = configs.shouldStringifyContent(filePath);
|
||||
var babelJest = shouldStringifyContent ? undefined : configs.babelJestTransformer;
|
||||
var isDefinitionFile = filePath.endsWith(constants_1.DECLARATION_TYPE_EXT);
|
||||
var isJsFile = constants_1.JS_JSX_REGEX.test(filePath);
|
||||
var isTsFile = !isDefinitionFile && constants_1.TS_TSX_REGEX.test(filePath);
|
||||
if (stringify) {
|
||||
result = "module.exports=" + JSON.stringify(source);
|
||||
if (shouldStringifyContent) {
|
||||
result = "module.exports=" + json_1.stringify(source);
|
||||
}
|
||||
else if (isDefinitionFile) {
|
||||
result = '';
|
||||
@@ -103,13 +103,12 @@ var TsJestTransformer = (function () {
|
||||
return result;
|
||||
};
|
||||
TsJestTransformer.prototype.getCacheKey = function (fileContent, filePath, _jestConfigStr, transformOptions) {
|
||||
this.logger.debug({ fileName: filePath, transformOptions: transformOptions }, 'computing cache key for', filePath);
|
||||
var configs = this.configsFor(transformOptions.config);
|
||||
this.logger.debug({ fileName: filePath, transformOptions: transformOptions }, 'computing cache key for', filePath);
|
||||
var _a = transformOptions.instrument, instrument = _a === void 0 ? false : _a, _b = transformOptions.rootDir, rootDir = _b === void 0 ? configs.rootDir : _b;
|
||||
return sha1_1.sha1(configs.cacheKey, '\x00', rootDir, '\x00', "instrument:" + (instrument ? 'on' : 'off'), '\x00', fileContent, '\x00', filePath);
|
||||
return sha1_1.sha1(this._transformCfgStr, '\x00', rootDir, '\x00', "instrument:" + (instrument ? 'on' : 'off'), '\x00', fileContent, '\x00', filePath);
|
||||
};
|
||||
TsJestTransformer._configSetsIndex = [];
|
||||
TsJestTransformer._lastTransformerId = 0;
|
||||
TsJestTransformer._cachedConfigSets = [];
|
||||
return TsJestTransformer;
|
||||
}());
|
||||
exports.TsJestTransformer = TsJestTransformer;
|
||||
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
export interface RawCompilerOptions {
|
||||
charset?: string;
|
||||
composite?: boolean;
|
||||
declaration?: boolean;
|
||||
declarationDir?: string | null;
|
||||
diagnostics?: boolean;
|
||||
disableReferencedProjectLoad?: boolean;
|
||||
emitBOM?: boolean;
|
||||
emitDeclarationOnly?: boolean;
|
||||
incremental?: boolean;
|
||||
tsBuildInfoFile?: string;
|
||||
inlineSourceMap?: boolean;
|
||||
inlineSources?: boolean;
|
||||
jsx?: 'preserve' | 'react' | 'react-jsx' | 'react-jsxdev' | 'react-native';
|
||||
reactNamespace?: string;
|
||||
jsxFactory?: string;
|
||||
jsxFragmentFactory?: string;
|
||||
jsxImportSource?: string;
|
||||
listFiles?: boolean;
|
||||
mapRoot?: string;
|
||||
module?: 'CommonJS' | 'AMD' | 'System' | 'UMD' | 'ES6' | 'ES2015' | 'ES2020' | 'ESNext' | 'None' | string;
|
||||
moduleResolution?: 'Classic' | 'Node';
|
||||
newLine?: 'crlf' | 'lf';
|
||||
noEmit?: boolean;
|
||||
noEmitHelpers?: boolean;
|
||||
noEmitOnError?: boolean;
|
||||
noImplicitAny?: boolean;
|
||||
noImplicitThis?: boolean;
|
||||
noUnusedLocals?: boolean;
|
||||
noUnusedParameters?: boolean;
|
||||
noLib?: boolean;
|
||||
noResolve?: boolean;
|
||||
noStrictGenericChecks?: boolean;
|
||||
skipDefaultLibCheck?: boolean;
|
||||
skipLibCheck?: boolean;
|
||||
outFile?: string;
|
||||
outDir?: string;
|
||||
preserveConstEnums?: boolean;
|
||||
preserveSymlinks?: boolean;
|
||||
preserveWatchOutput?: boolean;
|
||||
pretty?: boolean;
|
||||
removeComments?: boolean;
|
||||
rootDir?: string;
|
||||
isolatedModules?: boolean;
|
||||
sourceMap?: boolean;
|
||||
sourceRoot?: string;
|
||||
suppressExcessPropertyErrors?: boolean;
|
||||
suppressImplicitAnyIndexErrors?: boolean;
|
||||
target?: 'ES3' | 'ES5' | 'ES6' | 'ES2015' | 'ES2016' | 'ES2017' | 'ES2018' | 'ES2019' | 'ES2020' | 'ESNext' | string;
|
||||
watch?: boolean;
|
||||
fallbackPolling?: 'fixedPollingInterval' | 'priorityPollingInterval' | 'dynamicPriorityPolling';
|
||||
watchDirectory?: 'useFsEvents' | 'fixedPollingInterval' | 'dynamicPriorityPolling';
|
||||
watchFile?: 'fixedPollingInterval' | 'priorityPollingInterval' | 'dynamicPriorityPolling' | 'useFsEvents' | 'useFsEventsOnParentDirectory';
|
||||
experimentalDecorators?: boolean;
|
||||
emitDecoratorMetadata?: boolean;
|
||||
allowUnusedLabels?: boolean;
|
||||
noImplicitReturns?: boolean;
|
||||
noUncheckedIndexedAccess?: boolean;
|
||||
noFallthroughCasesInSwitch?: boolean;
|
||||
allowUnreachableCode?: boolean;
|
||||
forceConsistentCasingInFileNames?: boolean;
|
||||
generateCpuProfile?: string;
|
||||
baseUrl?: string;
|
||||
paths?: {
|
||||
[k: string]: string[];
|
||||
};
|
||||
plugins?: {
|
||||
name?: string;
|
||||
[k: string]: unknown;
|
||||
}[];
|
||||
rootDirs?: string[];
|
||||
typeRoots?: string[];
|
||||
types?: string[];
|
||||
traceResolution?: boolean;
|
||||
allowJs?: boolean;
|
||||
noErrorTruncation?: boolean;
|
||||
allowSyntheticDefaultImports?: boolean;
|
||||
noImplicitUseStrict?: boolean;
|
||||
listEmittedFiles?: boolean;
|
||||
disableSizeLimit?: boolean;
|
||||
lib?: ('ES5' | 'ES6' | 'ES2015' | 'ES2015.Collection' | 'ES2015.Core' | 'ES2015.Generator' | 'ES2015.Iterable' | 'ES2015.Promise' | 'ES2015.Proxy' | 'ES2015.Reflect' | 'ES2015.Symbol.WellKnown' | 'ES2015.Symbol' | 'ES2016' | 'ES2016.Array.Include' | 'ES2017' | 'ES2017.Intl' | 'ES2017.Object' | 'ES2017.SharedMemory' | 'ES2017.String' | 'ES2017.TypedArrays' | 'ES2018' | 'ES2018.AsyncGenerator' | 'ES2018.AsyncIterable' | 'ES2018.Intl' | 'ES2018.Promise' | 'ES2018.Regexp' | 'ES2019' | 'ES2019.Array' | 'ES2019.Object' | 'ES2019.String' | 'ES2019.Symbol' | 'ES2020' | 'ES2020.BigInt' | 'ES2020.Promise' | 'ES2020.String' | 'ES2020.Symbol.WellKnown' | 'ESNext' | 'ESNext.Array' | 'ESNext.AsyncIterable' | 'ESNext.BigInt' | 'ESNext.Intl' | 'ESNext.Promise' | 'ESNext.String' | 'ESNext.Symbol' | 'DOM' | 'DOM.Iterable' | 'ScriptHost' | 'WebWorker' | 'WebWorker.ImportScripts')[];
|
||||
strictNullChecks?: boolean;
|
||||
maxNodeModuleJsDepth?: number;
|
||||
importHelpers?: boolean;
|
||||
importsNotUsedAsValues?: 'remove' | 'preserve' | 'error';
|
||||
alwaysStrict?: boolean;
|
||||
strict?: boolean;
|
||||
strictBindCallApply?: boolean;
|
||||
downlevelIteration?: boolean;
|
||||
checkJs?: boolean;
|
||||
strictFunctionTypes?: boolean;
|
||||
strictPropertyInitialization?: boolean;
|
||||
esModuleInterop?: boolean;
|
||||
allowUmdGlobalAccess?: boolean;
|
||||
keyofStringsOnly?: boolean;
|
||||
useDefineForClassFields?: boolean;
|
||||
declarationMap?: boolean;
|
||||
resolveJsonModule?: boolean;
|
||||
assumeChangesOnlyAffectDirectDependencies?: boolean;
|
||||
extendedDiagnostics?: boolean;
|
||||
listFilesOnly?: boolean;
|
||||
disableSourceOfProjectReferenceRedirect?: boolean;
|
||||
disableSolutionSearching?: boolean;
|
||||
[k: string]: unknown;
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user