├── .eslintrc.js ├── .github └── workflows │ └── build.yml ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── LICENSE.md ├── README.md ├── jest.config.js ├── package.json ├── src ├── __test__ │ ├── downloadSpecHelper.json │ └── index.spec.ts └── index.ts ├── tsconfig.json └── yarn.lock /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['eslint-config-react-typescript'], 3 | env: { 4 | node: true, 5 | }, 6 | parserOptions: { 7 | project: 'tsconfig.json', 8 | sourceType: 'module', 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | 2 | # This is a basic workflow to help you get started with Actions 3 | 4 | name: Build 5 | 6 | # Controls when the action will run. Triggers the workflow on push or pull request 7 | # events but only for the master branch 8 | on: 9 | push: 10 | branches: 11 | - master 12 | 13 | # A workflow run is made up of one or more jobs that can run sequentially or in parallel 14 | jobs: 15 | # This workflow contains a single job called "build" 16 | build: 17 | # The type of runner that the job will run on 18 | runs-on: ubuntu-latest 19 | 20 | # Steps represent a sequence of tasks that will be executed as part of the job 21 | steps: 22 | # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it 23 | - name: Checkout repository 24 | uses: actions/checkout@v2 25 | - name: Set up Node.js 26 | uses: actions/setup-node@v1 27 | with: 28 | node-version: "14" 29 | registry-url: "https://registry.npmjs.org" 30 | - name: Install dependencies 31 | run: yarn install 32 | - name: Build 33 | run: yarn build 34 | - name: Config git user 35 | run: | 36 | git config --local user.email "ruanyu1@gmail.com" 37 | git config --local user.name "Yulong Ruan" 38 | - name: Tag release 39 | run: yarn release 40 | - name: Push to master 41 | run: git push --follow-tags origin master 42 | - name: Publish to NPM 43 | run: npm publish --access public 44 | env: 45 | NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | coverage 4 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src/ 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ### 0.2.7 (2021-10-12) 6 | 7 | 8 | ### Bug Fixes 9 | 10 | * download on watch run avoiding multiple downloads by simple args memo ([#12](https://github.com/ruanyl/webpack-remote-types-plugin/issues/12)) ([1c1fd5d](https://github.com/ruanyl/webpack-remote-types-plugin/commit/1c1fd5d991bd8f6ae95620e9bb64220655123da4)) 11 | 12 | ### 0.2.6 (2021-09-02) 13 | 14 | ### 0.2.5 (2021-07-18) 15 | 16 | 17 | ### Features 18 | 19 | * support remote types served by https ([#8](https://github.com/ruanyl/webpack-remote-types-plugin/issues/8)) ([4cc8f24](https://github.com/ruanyl/webpack-remote-types-plugin/commit/4cc8f244a7bfc900aad5881021c7030f6bd40ca7)), closes [#7](https://github.com/ruanyl/webpack-remote-types-plugin/issues/7) 20 | 21 | ### 0.2.4 (2021-07-13) 22 | 23 | 24 | ### Bug Fixes 25 | 26 | * avoid double downloads by changing used hook ([#3](https://github.com/ruanyl/webpack-remote-types-plugin/issues/3)) ([1c8b703](https://github.com/ruanyl/webpack-remote-types-plugin/commit/1c8b703abb8acebfcb55baffc9c94e849c48e8ed)) 27 | 28 | ### 0.2.2 (2021-07-13) 29 | 30 | 31 | ### Bug Fixes 32 | 33 | * remove temp .tgz after extract ([abaa6df](https://github.com/ruanyl/webpack-remote-types-plugin/commit/abaa6dfed55d4806a3e4d5930973e71d8f39005f)) 34 | 35 | ### 0.2.1 (2021-06-16) 36 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Yulong Ruan 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## WebpackRemoteTypesPlugin 2 | 3 | A webpack plugin to download typescript type definitions generated by [dts-loader](https://github.com/ruanyl/dts-loader) 4 | from remote. 5 | 6 | ### Install 7 | 8 | ``` 9 | yarn add webpack-remote-types-plugin 10 | ``` 11 | 12 | ### Usage 13 | 14 | ```javascript 15 | new WebpackRemoteTypesPlugin({ 16 | remotes: { 17 | app: 'app@http://localhost:9000/remoteEntry.js', 18 | }, 19 | outputDir: 'types', // supports [name] as the remote name 20 | remoteFileName: '[name]-dts.tgz' // default filename is [name]-dts.tgz where [name] is the remote name, for example, `app` with the above setup 21 | }), 22 | ``` 23 | 24 | The plugin will download tarball from `http://localhost:9000/app-dts.tgz` and unzip the tarball to `./types` folder 25 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | transform: { 4 | '^.+\\.tsx?$': 'ts-jest', 5 | }, 6 | moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], 7 | testRegex: 'src/.*/?__test__/.*\\.spec.tsx?$', 8 | coverageDirectory: 'coverage', 9 | collectCoverageFrom: ['src/**/*.{ts,tsx,js,jsx}', '!src/**/*.d.ts'], 10 | } 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-remote-types-plugin", 3 | "version": "0.2.7", 4 | "description": "webpack plugin to download type definitions based on remotes", 5 | "main": "./lib/index.js", 6 | "types": "./lib/index.d.ts", 7 | "scripts": { 8 | "build": "tsc --declaration", 9 | "prepublish": "npm run build", 10 | "release": "standard-version --releaseCommitMessageFormat 'chore(release): {{currentTag}} [skip ci]'", 11 | "test": "jest" 12 | }, 13 | "devDependencies": { 14 | "@tsconfig/node14": "^1.0.0", 15 | "@types/fs-extra": "^9.0.11", 16 | "@types/jest": "^26.0.23", 17 | "@types/tar": "^4.0.4", 18 | "@typescript-eslint/eslint-plugin": "^4.25.0", 19 | "@typescript-eslint/parser": "^4.23.0", 20 | "eslint": "^7.27.0", 21 | "eslint-config-react-typescript": "^1.0.7", 22 | "import-sort-cli": "^6.0.0", 23 | "import-sort-parser-typescript": "^6.0.0", 24 | "import-sort-style-alias": "^1.2.0", 25 | "jest": "^26.6.3", 26 | "prettier": "^2.3.0", 27 | "standard-version": "^9.3.0", 28 | "ts-jest": "^26.5.6", 29 | "typescript": "^4.2", 30 | "webpack": "^5.37.1" 31 | }, 32 | "dependencies": { 33 | "fs-extra": "^10.0.0", 34 | "tar": "^6.1.0" 35 | }, 36 | "peerDependencies": {}, 37 | "prettier": { 38 | "trailingComma": "es5", 39 | "tabWidth": 2, 40 | "semi": false, 41 | "singleQuote": true 42 | }, 43 | "author": "Yulong Ruan ", 44 | "license": "MIT", 45 | "keywords": [ 46 | "typescript" 47 | ], 48 | "repository": { 49 | "type": "git", 50 | "url": "https://github.com/ruanyl/webpack-remote-types-plugin.git" 51 | }, 52 | "homepage": "https://github.com/ruanyl/webpack-remote-types-plugin", 53 | "bugs": "https://github.com/ruanyl/webpack-remote-types-plugin/issues" 54 | } 55 | -------------------------------------------------------------------------------- /src/__test__/downloadSpecHelper.json: -------------------------------------------------------------------------------- 1 | { 2 | "userId": 1, 3 | "id": 1, 4 | "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit", 5 | "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto" 6 | } -------------------------------------------------------------------------------- /src/__test__/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { downloadFile } from '../index' 2 | import path from 'path' 3 | 4 | describe('download file', () => { 5 | test('can download https files', async () => { 6 | const isSuccess = await downloadFile('https://jsonplaceholder.typicode.com/posts/1', path.resolve('src/__test__', 'downloadSpecHelper.json')) 7 | expect(isSuccess).toBe(true); 8 | }); 9 | test('can download http files', async () => { 10 | const isSuccess = await downloadFile('http://jsonplaceholder.typicode.com/posts/1', path.resolve('src/__test__', 'downloadSpecHelper.json')) 11 | expect(isSuccess).toBe(true); 12 | }); 13 | }) -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import { Compiler } from 'webpack' 2 | import { URL } from 'url' 3 | import fs from 'fs-extra' 4 | import https from 'https' 5 | import http from 'http' 6 | import path from 'path' 7 | import tar from 'tar' 8 | 9 | const cwd = process.cwd() 10 | 11 | export function downloadFile(url: string, targetPath: string) { 12 | const get = url.includes('https://') ? https.get : http.get 13 | 14 | return new Promise((resolve) => { 15 | const target = fs.createWriteStream(targetPath) 16 | get(url, (response) => { 17 | response 18 | .pipe(target) 19 | .on('close', () => { 20 | resolve(true) 21 | }) 22 | .on('finish', () => { 23 | resolve(true) 24 | }) 25 | .on('error', () => { 26 | resolve(false) 27 | }) 28 | }) 29 | }) 30 | } 31 | 32 | async function downloadFederationTypes( 33 | remotes: Record, 34 | outputDir: string, 35 | remoteFileName: string = '[name]-dts.tgz' 36 | ) { 37 | // Paths of downloaded dts zip files 38 | const savedDts: string[] = [] 39 | 40 | for (const [, value] of Object.entries(remotes)) { 41 | const [moduleName, entryFileURL] = value.split('@') 42 | const outputPath = outputDir.replace('[name]', moduleName) 43 | const dtsFileName = remoteFileName.replace('[name]', moduleName) 44 | const dtsFileURL = new URL(dtsFileName, entryFileURL).href 45 | const targetFile = path.resolve(outputPath, dtsFileName) 46 | 47 | await fs.ensureDir(outputPath) 48 | 49 | console.log('Downloading: ', dtsFileURL) 50 | const saved = await downloadFile(dtsFileURL, targetFile) 51 | if (saved) { 52 | savedDts.push(targetFile) 53 | console.log('Saved: ', targetFile) 54 | await tar.x({ 55 | file: targetFile, 56 | cwd: outputPath, 57 | }) 58 | await fs.remove(targetFile) 59 | } else { 60 | console.error('Failed to download remote DTS: ', dtsFileURL) 61 | } 62 | } 63 | } 64 | 65 | interface Options { 66 | remotes: Record 67 | outputDir: string 68 | remoteFileName?: string 69 | } 70 | 71 | export default class WebpackRemoteTypesPlugin { 72 | options: Options 73 | private remotes: Record = {} 74 | private outputDir: string = '' 75 | private remoteFileName: string | undefined 76 | 77 | constructor(options: Options) { 78 | this.options = options 79 | } 80 | 81 | apply(compiler: Compiler) { 82 | const tapCallback = async () => { 83 | const { remotes, remoteFileName, outputDir } = this.options 84 | const output = path.resolve(cwd, outputDir) 85 | const paramsChanged = this.remotes !== remotes || this.outputDir !== output || this.remoteFileName !== remoteFileName 86 | if (paramsChanged) { 87 | await downloadFederationTypes(remotes, output, remoteFileName) 88 | this.remotes = remotes 89 | this.outputDir = output 90 | this.remoteFileName = remoteFileName 91 | } 92 | } 93 | 94 | compiler.hooks.beforeRun.tapPromise('WebpackRemoteTypesPlugin', tapCallback) 95 | compiler.hooks.watchRun.tapPromise('WebpackRemoteTypesPlugin', tapCallback) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node14/tsconfig.json", 3 | "compilerOptions": { 4 | "preserveConstEnums": true, 5 | "removeComments": true, 6 | "outDir": "lib", 7 | "noUnusedLocals": true, 8 | "noUnusedParameters": true 9 | }, 10 | "include": [ 11 | "src/**/*" 12 | ] 13 | } 14 | --------------------------------------------------------------------------------