webpack-federation-types-plugin 1.2.2 → 1.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,196 +1,196 @@
1
- const validate = require("schema-utils").validate
2
- const ts = require("typescript")
3
- const axios = require("axios")
4
- const fs = require("fs-extra")
5
- const path = require("path")
6
- const ms = require("ms")
7
-
8
- let isFirstCompilation = true
9
- const PLUGIN_NAME = "FederationTypesPlugin"
10
- const MF_TYPES_DIR = "federation-types"
11
- const DECLARATION_FILE_EXT = ".d.ts"
12
-
13
- const tscOptions = {
14
- allowJs: true,
15
- declaration: true,
16
- emitDeclarationOnly: true,
17
- }
18
-
19
- const optionsSchema = {
20
- type: "object",
21
- properties: {
22
- excludeRemotes: {
23
- type: "array",
24
- },
25
- exposeTypes: {
26
- type: "boolean",
27
- },
28
- importTypes: {
29
- type: "boolean",
30
- },
31
- getTypesInterval: {
32
- type: "string",
33
- },
34
- },
35
- }
36
-
37
- const getAssetData = (content) => {
38
- const fileBuffer = Buffer.from(content, "utf-8")
39
- const fileData = {
40
- source: () => fileBuffer,
41
- size: () => fileBuffer.length,
42
- }
43
- return fileData
44
- }
45
-
46
- class FederationTypesPlugin {
47
- constructor(options = {}) {
48
- validate(optionsSchema, options)
49
- this._options = options
50
- }
51
-
52
- /**
53
- * @param {import("webpack").Compiler} compiler
54
- */
55
- apply(compiler) {
56
- let getTypesInterval
57
- const federationPlugin =
58
- compiler.options.plugins && compiler.options.plugins.find((plugin) => plugin.constructor.name === "ModuleFederationPlugin")
59
- if (!federationPlugin) throw new Error("No ModuleFederationPlugin found.")
60
- const logger = compiler.getInfrastructureLogger(PLUGIN_NAME)
61
-
62
- const exposes = Object.fromEntries(Object.entries(federationPlugin._options.exposes || {}).map(([moduleName, currentPath]) => {
63
- let isIndexFile = false
64
- const absPath = path.resolve(process.cwd(), currentPath)
65
- const hasExtension = !!path.extname(currentPath)
66
- const extension = hasExtension ? "" : fs.existsSync(absPath + ".jsx") ? ".jsx" : fs.existsSync(absPath + ".js") ? ".js" : ""
67
- if (!hasExtension && extension === "") {
68
- if ( fs.existsSync(path.resolve(absPath, "index.js"))) {
69
- isIndexFile = true
70
- }
71
- else logger.error(`Couldn't find ${currentPath}`)
72
- }
73
-
74
- return [moduleName.replace("./", ""), currentPath + (isIndexFile ? "/index.js" : "") + extension]
75
- }
76
- ))
77
-
78
- const modulesPathsMap = Object.fromEntries(
79
- Object.entries(exposes).map(([name, currentPath]) => {
80
- return [name, path.join(process.cwd(), currentPath).replace(/\\/g, "/")]
81
- })
82
- )
83
-
84
- const declarationFileToExposeNameMap = Object.entries(modulesPathsMap).reduce(
85
- (result, [key, val]) => Object.assign(result, {[val.replace(/\.jsx?$/, DECLARATION_FILE_EXT)]: key}),
86
- {}
87
- )
88
-
89
- const modulesPaths = Object.values(modulesPathsMap)
90
-
91
- const createTypesDefinitions = (compilation) => {
92
- const host = ts.createCompilerHost(tscOptions)
93
-
94
- const createdFiles = {}
95
- host.writeFile = (fileName, fileContent) => {
96
- const isExposedModules = modulesPaths.some((current) => current.startsWith(fileName.replace(/\.d\.ts/, "")))
97
- if (isExposedModules) {
98
- createdFiles[fileName] = fileContent
99
- }
100
- }
101
-
102
- const program = ts.createProgram(modulesPaths, tscOptions, host)
103
- program.emit()
104
-
105
- // create definitions files
106
- modulesPaths.forEach((modulePath) => {
107
- const typeDefFilePath = modulePath.replace(/\.jsx?$/, DECLARATION_FILE_EXT)
108
- const typeDefFileContent = createdFiles[typeDefFilePath]
109
- if (typeDefFileContent) {
110
- compilation.emitAsset(
111
- path.join(MF_TYPES_DIR, declarationFileToExposeNameMap[typeDefFilePath] + DECLARATION_FILE_EXT),
112
- getAssetData(typeDefFileContent)
113
- )
114
- } else {
115
- logger.warn(`failed to create ${typeDefFilePath}`)
116
- }
117
- })
118
-
119
- // create index.json
120
- const modulesNames = Object.keys(exposes).map((moduleName) => moduleName + DECLARATION_FILE_EXT)
121
- compilation.emitAsset(path.join(MF_TYPES_DIR, "index.json"), getAssetData(JSON.stringify(modulesNames)))
122
- }
123
-
124
- const getRemoteDeclareDirPath = (remoteName) => path.resolve(process.cwd(), "node_modules", "@types", remoteName)
125
-
126
- const getTypesDefinitions = async () => {
127
- const remotes = federationPlugin._options.remotes
128
- if (!remotes) return
129
-
130
- const excludeRemotes = this._options.excludeRemotes || []
131
- const wantedRemotes = Object.entries(remotes).filter((remote) => !excludeRemotes.find((c) => c === remote[0]))
132
-
133
- for (const [remoteName, remoteEntryUri] of wantedRemotes) {
134
- const {origin, pathname} = new URL(remoteEntryUri.split("@")[1])
135
- const remotePublicUrl = origin + pathname.slice(0, pathname.lastIndexOf("/") + 1)
136
- const remoteDeclareDirPath = getRemoteDeclareDirPath(remoteName)
137
- const federationTypesUrl = remotePublicUrl + MF_TYPES_DIR + "/"
138
-
139
- axios
140
- // get types index from the current remote
141
- .get(federationTypesUrl + "index.json")
142
- .catch((error) => {
143
- if (error.response?.status === 404) logger.warn(`WARNING: The remote ${remoteName} has no types`)
144
- else logger.error(`Failed to get remote types from ${remotePublicUrl}.`, error.message)
145
- })
146
- .then((response) => response?.data)
147
- .then((modulesNames) => {
148
- if (!modulesNames) return
149
- // for each remote module get his types
150
- modulesNames.forEach((moduleName) => {
151
- const moduleDeclarationFileUrl = federationTypesUrl + moduleName
152
- axios
153
- .get(moduleDeclarationFileUrl)
154
- .catch((error) => {
155
- logger.error(`Failed to get ${moduleDeclarationFileUrl} ${error.message}`)
156
- })
157
- .then((declarationFileResponse) => {
158
- if (!declarationFileResponse) return
159
- const declarationFileContent = declarationFileResponse.data
160
- const decFilePath = path.join(remoteDeclareDirPath, moduleName)
161
- fs.promises
162
- .mkdir(path.dirname(decFilePath), {recursive: true})
163
- .catch((error) => logger.error(`Failed to write dir: ${decFilePath}`, error))
164
- .then(() => fs.writeFile(decFilePath, declarationFileContent, {recursive: true}))
165
- .catch((error) => logger.error(`Failed to write declaration file: ${decFilePath}`, error))
166
- })
167
- .catch((error) => {
168
- logger.error(`Failed to get ${moduleDeclarationFileUrl} ${error.message}`)
169
- })
170
- })
171
- })
172
- .catch((error) => {
173
- logger.error("Failed to add declaration file", error.message)
174
- })
175
- }
176
- }
177
-
178
- compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
179
- const pluginOptions = this._options || {}
180
- compilation.hooks.beforeCodeGeneration.tap(PLUGIN_NAME, () => {
181
- if (!isFirstCompilation) return
182
- isFirstCompilation = false
183
- if (pluginOptions.exposeTypes !== false) createTypesDefinitions(compilation)
184
- if (compilation.options.mode === "development" && pluginOptions.importTypes !== false) {
185
- getTypesDefinitions()
186
- if (pluginOptions.getTypesInterval) {
187
- clearInterval(getTypesInterval)
188
- getTypesInterval = setInterval(() => getTypesDefinitions(compilation), ms(pluginOptions.getTypesInterval))
189
- }
190
- }
191
- })
192
- })
193
- }
194
- }
195
-
196
- module.exports = FederationTypesPlugin
1
+ const validate = require("schema-utils").validate
2
+ const ts = require("typescript")
3
+ const axios = require("axios")
4
+ const fs = require("fs-extra")
5
+ const path = require("path")
6
+ const ms = require("ms")
7
+
8
+ let isFirstCompilation = true
9
+ const PLUGIN_NAME = "FederationTypesPlugin"
10
+ const MF_TYPES_DIR = "federation-types"
11
+ const DECLARATION_FILE_EXT = ".d.ts"
12
+
13
+ const tscOptions = {
14
+ allowJs: true,
15
+ declaration: true,
16
+ emitDeclarationOnly: true,
17
+ }
18
+
19
+ const optionsSchema = {
20
+ type: "object",
21
+ properties: {
22
+ excludeRemotes: {
23
+ type: "array",
24
+ },
25
+ exposeTypes: {
26
+ type: "boolean",
27
+ },
28
+ importTypes: {
29
+ type: "boolean",
30
+ },
31
+ getTypesInterval: {
32
+ type: "string",
33
+ },
34
+ },
35
+ }
36
+
37
+ const getAssetData = (content) => {
38
+ const fileBuffer = Buffer.from(content, "utf-8")
39
+ const fileData = {
40
+ source: () => fileBuffer,
41
+ size: () => fileBuffer.length,
42
+ }
43
+ return fileData
44
+ }
45
+
46
+ class FederationTypesPlugin {
47
+ constructor(options = {}) {
48
+ validate(optionsSchema, options)
49
+ this._options = options
50
+ }
51
+
52
+ /**
53
+ * @param {import("webpack").Compiler} compiler
54
+ */
55
+ apply(compiler) {
56
+ let getTypesInterval
57
+ const federationPlugin =
58
+ compiler.options.plugins && compiler.options.plugins.find((plugin) => plugin.constructor.name === "ModuleFederationPlugin")
59
+ if (!federationPlugin) throw new Error("No ModuleFederationPlugin found.")
60
+ const logger = compiler.getInfrastructureLogger(PLUGIN_NAME)
61
+
62
+ const exposes = Object.fromEntries(Object.entries(federationPlugin._options.exposes || {}).map(([moduleName, currentPath]) => {
63
+ let isIndexFile = false
64
+ const absPath = path.resolve(process.cwd(), currentPath)
65
+ const hasExtension = !!path.extname(currentPath)
66
+ const extension = hasExtension ? "" : fs.existsSync(absPath + ".jsx") ? ".jsx" : fs.existsSync(absPath + ".js") ? ".js" : ""
67
+ if (!hasExtension && extension === "") {
68
+ if ( fs.existsSync(path.resolve(absPath, "index.js"))) {
69
+ isIndexFile = true
70
+ }
71
+ else logger.error(`Couldn't find ${currentPath}`)
72
+ }
73
+
74
+ return [moduleName.replace("./", ""), currentPath + (isIndexFile ? "/index.js" : "") + extension]
75
+ }
76
+ ))
77
+
78
+ const modulesPathsMap = Object.fromEntries(
79
+ Object.entries(exposes).map(([name, currentPath]) => {
80
+ return [name, path.join(process.cwd(), currentPath).replace(/\\/g, "/")]
81
+ })
82
+ )
83
+
84
+ const declarationFileToExposeNameMap = Object.entries(modulesPathsMap).reduce(
85
+ (result, [key, val]) => Object.assign(result, {[val.replace(/\.jsx?$/, DECLARATION_FILE_EXT)]: key}),
86
+ {}
87
+ )
88
+
89
+ const modulesPaths = Object.values(modulesPathsMap)
90
+
91
+ const createTypesDefinitions = (compilation) => {
92
+ const host = ts.createCompilerHost(tscOptions)
93
+
94
+ const createdFiles = {}
95
+ host.writeFile = (fileName, fileContent) => {
96
+ const isExposedModules = modulesPaths.some((current) => current.startsWith(fileName.replace(/\.d\.ts/, "")))
97
+ if (isExposedModules) {
98
+ createdFiles[fileName] = fileContent
99
+ }
100
+ }
101
+
102
+ const program = ts.createProgram(modulesPaths, tscOptions, host)
103
+ program.emit()
104
+
105
+ // create definitions files
106
+ modulesPaths.forEach((modulePath) => {
107
+ const typeDefFilePath = modulePath.replace(/\.jsx?$/, DECLARATION_FILE_EXT)
108
+ const typeDefFileContent = createdFiles[typeDefFilePath]
109
+ if (typeDefFileContent) {
110
+ compilation.emitAsset(
111
+ path.join(MF_TYPES_DIR, declarationFileToExposeNameMap[typeDefFilePath] + DECLARATION_FILE_EXT),
112
+ getAssetData(typeDefFileContent)
113
+ )
114
+ } else {
115
+ logger.warn(`failed to create ${typeDefFilePath}`)
116
+ }
117
+ })
118
+
119
+ // create index.json
120
+ const modulesNames = Object.keys(exposes).map((moduleName) => moduleName + DECLARATION_FILE_EXT)
121
+ compilation.emitAsset(path.join(MF_TYPES_DIR, "index.json"), getAssetData(JSON.stringify(modulesNames)))
122
+ }
123
+
124
+ const getRemoteDeclareDirPath = (remoteName) => path.resolve(process.cwd(), "node_modules", "@types", remoteName)
125
+
126
+ const getTypesDefinitions = async () => {
127
+ const remotes = federationPlugin._options.remotes
128
+ if (!remotes) return
129
+
130
+ const excludeRemotes = this._options.excludeRemotes || []
131
+ const wantedRemotes = Object.entries(remotes).filter((remote) => !excludeRemotes.find((c) => c === remote[0]))
132
+
133
+ for (const [remoteName, remoteEntryUri] of wantedRemotes) {
134
+ const {origin, pathname} = new URL(remoteEntryUri.split("@")[1])
135
+ const remotePublicUrl = origin + pathname.slice(0, pathname.lastIndexOf("/") + 1)
136
+ const remoteDeclareDirPath = getRemoteDeclareDirPath(remoteName)
137
+ const federationTypesUrl = remotePublicUrl + MF_TYPES_DIR + "/"
138
+
139
+ axios
140
+ // get types index from the current remote
141
+ .get(federationTypesUrl + "index.json")
142
+ .catch((error) => {
143
+ if (error.response?.status === 404) logger.warn(`WARNING: The remote ${remoteName} has no types`)
144
+ else logger.warn(`Failed to get remote types from ${remotePublicUrl}.`, error.message)
145
+ })
146
+ .then((response) => response?.data)
147
+ .then((modulesNames) => {
148
+ if (!modulesNames) return
149
+ // for each remote module get his types
150
+ modulesNames.forEach((moduleName) => {
151
+ const moduleDeclarationFileUrl = federationTypesUrl + moduleName
152
+ axios
153
+ .get(moduleDeclarationFileUrl)
154
+ .catch((error) => {
155
+ logger.error(`Failed to get ${moduleDeclarationFileUrl} ${error.message}`)
156
+ })
157
+ .then((declarationFileResponse) => {
158
+ if (!declarationFileResponse) return
159
+ const declarationFileContent = declarationFileResponse.data
160
+ const decFilePath = path.join(remoteDeclareDirPath, moduleName)
161
+ fs.promises
162
+ .mkdir(path.dirname(decFilePath), {recursive: true})
163
+ .catch((error) => logger.error(`Failed to write dir: ${decFilePath}`, error))
164
+ .then(() => fs.writeFile(decFilePath, declarationFileContent, {recursive: true}))
165
+ .catch((error) => logger.error(`Failed to write declaration file: ${decFilePath}`, error))
166
+ })
167
+ .catch((error) => {
168
+ logger.error(`Failed to get ${moduleDeclarationFileUrl} ${error.message}`)
169
+ })
170
+ })
171
+ })
172
+ .catch((error) => {
173
+ logger.error("Failed to add declaration file", error.message)
174
+ })
175
+ }
176
+ }
177
+
178
+ compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
179
+ const pluginOptions = this._options || {}
180
+ compilation.hooks.beforeCodeGeneration.tap(PLUGIN_NAME, () => {
181
+ if (!isFirstCompilation) return
182
+ isFirstCompilation = false
183
+ if (pluginOptions.exposeTypes !== false) createTypesDefinitions(compilation)
184
+ if (compilation.options.mode === "development" && pluginOptions.importTypes !== false) {
185
+ getTypesDefinitions()
186
+ if (pluginOptions.getTypesInterval) {
187
+ clearInterval(getTypesInterval)
188
+ getTypesInterval = setInterval(() => getTypesDefinitions(compilation), ms(pluginOptions.getTypesInterval))
189
+ }
190
+ }
191
+ })
192
+ })
193
+ }
194
+ }
195
+
196
+ module.exports = FederationTypesPlugin
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "webpack-federation-types-plugin",
3
3
  "description": "This plugin adds type definitions for remote modules",
4
- "version": "1.2.2",
4
+ "version": "1.2.3",
5
5
  "main": "FederationTypesPlugin.js",
6
6
  "types": "types.d.ts",
7
7
  "scripts": {