glass-easel-devtools-extension 0.9.0

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.
package/.eslintignore ADDED
@@ -0,0 +1,3 @@
1
+ /node_modules
2
+ /dist
3
+ /pkg
package/LICENSE ADDED
@@ -0,0 +1,7 @@
1
+ Copyright 2024 wechat-miniprogram
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.
package/README.md ADDED
@@ -0,0 +1,16 @@
1
+ # glass-easel DevTools for Browsers
2
+
3
+ This is the DevTools panel implementation of glass-easel-devtools for Chrome/Firefox DevTools.
4
+
5
+ See [glass-easel-devtools](https://github.com/wechat-miniprogram/glass-easel-devtools) for details.
6
+
7
+ ## How to Build
8
+
9
+ [nodejs](https://nodejs.org/en) and [pnpm](https://pnpm.io/) should be globally installed.
10
+
11
+ In the project root directory (glass-easel-devtools):
12
+
13
+ 1. run `pnpm install` to install dependencies;
14
+ 2. run `npm run pack-extension` to build.
15
+
16
+ The final product can be found in `extension/pkg` directory.
@@ -0,0 +1,28 @@
1
+ {
2
+ "description": "DevTools extension for developing glass-easel applications",
3
+ "manifest_version": 3,
4
+ "name": "glass-easel DevTools",
5
+ "version": "0.9.0",
6
+ "homepage_url": "https://github.com/wechat-miniprogram/glass-easel",
7
+ "icons": {
8
+ "16": "icons/glass-easel-16.png",
9
+ "32": "icons/glass-easel-32.png",
10
+ "48": "icons/glass-easel-48.png",
11
+ "64": "icons/glass-easel-64.png",
12
+ "128": "icons/glass-easel-128.png",
13
+ "256": "icons/glass-easel-256.png",
14
+ "512": "icons/glass-easel-512.png"
15
+ },
16
+ "permissions": [
17
+ "activeTab",
18
+ "scripting",
19
+ "webNavigation"
20
+ ],
21
+ "host_permissions": [
22
+ "<all_urls>"
23
+ ],
24
+ "background": {
25
+ "service_worker": "dist/background.js"
26
+ },
27
+ "devtools_page": "dist/devtools.html"
28
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "description": "DevTools extension for developing glass-easel applications",
3
+ "manifest_version": 3,
4
+ "name": "glass-easel DevTools",
5
+ "version": "0.9.0",
6
+ "homepage_url": "https://github.com/wechat-miniprogram/glass-easel",
7
+ "icons": {
8
+ "16": "icons/glass-easel-16.png",
9
+ "32": "icons/glass-easel-32.png",
10
+ "48": "icons/glass-easel-48.png",
11
+ "64": "icons/glass-easel-64.png",
12
+ "128": "icons/glass-easel-128.png",
13
+ "256": "icons/glass-easel-256.png",
14
+ "512": "icons/glass-easel-512.png"
15
+ },
16
+ "permissions": [
17
+ "activeTab",
18
+ "scripting",
19
+ "webNavigation"
20
+ ],
21
+ "host_permissions": [
22
+ "<all_urls>"
23
+ ],
24
+ "background": {
25
+ "scripts": ["dist/background.js"]
26
+ },
27
+ "devtools_page": "dist/devtools.html"
28
+ }
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/pack.js ADDED
@@ -0,0 +1,74 @@
1
+ /* eslint-disable import/no-extraneous-dependencies */
2
+
3
+ const fs = require('fs')
4
+ const path = require('path')
5
+ const archiver = require('archiver')
6
+
7
+ fs.mkdirSync(path.join(__dirname, 'pkg'), { recursive: true })
8
+
9
+ const createZip = (outName, dirs, files) =>
10
+ new Promise((resolve) => {
11
+ const output = fs.createWriteStream(path.join(__dirname, 'pkg', outName))
12
+ const archive = archiver('zip', {
13
+ zlib: { level: 9 },
14
+ })
15
+ output.on('close', () => {
16
+ resolve()
17
+ })
18
+ output.on('warning', (err) => {
19
+ throw err
20
+ })
21
+ output.on('error', (err) => {
22
+ throw err
23
+ })
24
+ archive.pipe(output)
25
+ Object.entries(dirs).forEach(([key, value]) => {
26
+ archive.directory(key, value)
27
+ })
28
+ Object.entries(files).forEach(([key, value]) => {
29
+ archive.file(key, { name: value })
30
+ })
31
+ archive.finalize()
32
+ })
33
+
34
+ createZip(
35
+ 'glass-easel-devtools-chrome.zip',
36
+ { dist: 'dist', icons: 'icons' },
37
+ { 'chrome.manifest.json': 'manifest.json' },
38
+ )
39
+
40
+ createZip(
41
+ 'glass-easel-devtools-firefox.zip',
42
+ { dist: 'dist', icons: 'icons' },
43
+ { 'firefox.manifest.json': 'manifest.json' },
44
+ )
45
+
46
+ createZip(
47
+ 'source.zip',
48
+ {
49
+ src: 'extension/src',
50
+ icons: 'extension/icons',
51
+ '../agent/src': 'agent/src',
52
+ '../panel/src': 'panel/src',
53
+ '../panel/typings': 'panel/typings',
54
+ },
55
+ {
56
+ 'README.md': 'README.md',
57
+ 'package.json': 'extension/package.json',
58
+ 'tsconfig.json': 'extension/tsconfig.json',
59
+ 'webpack.config.js': 'extension/webpack.config.js',
60
+ 'pack.js': 'extension/pack.js',
61
+ '../package.json': 'package.json',
62
+ '../pnpm-lock.yaml': 'pnpm-lock.yaml',
63
+ '../pnpm-workspace.yaml': 'pnpm-workspace.yaml',
64
+ '../tsconfig.json': 'tsconfig.json',
65
+ '../agent/package.json': 'agent/package.json',
66
+ '../agent/tsconfig.json': 'agent/tsconfig.json',
67
+ '../agent/webpack.config.js': 'agent/webpack.config.js',
68
+ '../agent/wxml_loader.js': 'agent/wxml_loader.js',
69
+ '../panel/package.json': 'panel/package.json',
70
+ '../panel/tsconfig.json': 'panel/tsconfig.json',
71
+ '../panel/webpack.config.js': 'panel/webpack.config.js',
72
+ '../panel/src.d.ts': 'panel/src.d.ts',
73
+ },
74
+ )
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "glass-easel-devtools-extension",
3
+ "version": "0.9.0",
4
+ "main": "src/utils.ts",
5
+ "dependencies": {
6
+ "glass-easel": "^0.9.0",
7
+ "glass-easel-devtools-agent": "0.9.0",
8
+ "glass-easel-devtools-panel": "0.9.0"
9
+ },
10
+ "devDependencies": {
11
+ "archiver": "^7.0.1",
12
+ "copy-webpack-plugin": "^12.0.2"
13
+ },
14
+ "scripts": {
15
+ "build": "webpack --config webpack.config.js --no-devtool",
16
+ "dev": "webpack --config webpack.config.js --mode development --watch",
17
+ "debug-chrome": "ln -s -f chrome.manifest.json manifest.json",
18
+ "debug-firefox": "ln -s -f firefox.manifest.json manifest.json"
19
+ }
20
+ }
@@ -0,0 +1,50 @@
1
+ import { getDevTools, type protocol } from 'glass-easel-devtools-agent'
2
+ import { type DevToolsBridge } from '../utils'
3
+
4
+ export type AgentSendMessageMeta = protocol.AgentSendMessage | { kind: '_init' }
5
+
6
+ if (window.top !== window) {
7
+ // for iframes, connect to the top frame
8
+ setTimeout(() => {
9
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
10
+ if ((window.top as any)?.__glassEaselDevTools__) {
11
+ const userTopGlobal = window.top as unknown as { __glassEaselDevTools__: DevToolsBridge }
12
+ const userGlobal = window as unknown as { __glassEaselDevTools__?: DevToolsBridge }
13
+ userGlobal.__glassEaselDevTools__?._devToolsConnect(userTopGlobal.__glassEaselDevTools__)
14
+ }
15
+ }, 0)
16
+ } else {
17
+ // receive the host element
18
+ const hostElement = document.querySelector('glass-easel-devtools')
19
+ if (!hostElement) throw new Error('Failed to initialize glass-easel DevTools agent')
20
+
21
+ // messaging with content script
22
+ const postMessage = (message: AgentSendMessageMeta) => {
23
+ const ev = new CustomEvent('glass-easel-devtools-agent-send', { detail: message })
24
+ hostElement.dispatchEvent(ev)
25
+ }
26
+ hostElement.addEventListener('glass-easel-devtools-agent-recv', (ev) => {
27
+ const { detail } = ev as CustomEvent<protocol.AgentRecvMessage>
28
+ requestListener?.(detail)
29
+ })
30
+
31
+ // create the real agent
32
+ let requestListener: ((data: protocol.AgentRecvMessage) => void) | null = null
33
+ const messageChannel = {
34
+ send(data: protocol.AgentSendMessage) {
35
+ postMessage(data)
36
+ },
37
+ recv(listener: (data: protocol.AgentRecvMessage) => void) {
38
+ requestListener = listener
39
+ },
40
+ }
41
+ const devTools = getDevTools(messageChannel)
42
+
43
+ setTimeout(() => {
44
+ const userGlobal = window as unknown as { __glassEaselDevTools__?: DevToolsBridge }
45
+ userGlobal.__glassEaselDevTools__?._devToolsConnect(devTools)
46
+ }, 0)
47
+
48
+ // send a message to indicate the agent ready
49
+ postMessage({ kind: '_init' })
50
+ }
@@ -0,0 +1,116 @@
1
+ import { type AgentSendMessageMeta } from '../agent'
2
+ import { type PanelSendMessageMeta } from '../panel'
3
+ import { ConnectionSource } from '../utils'
4
+
5
+ // inject a small user script
6
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
7
+ chrome.scripting.registerContentScripts([
8
+ {
9
+ id: 'glassEaselDevToolsUser',
10
+ world: 'MAIN',
11
+ matches: ['<all_urls>'],
12
+ allFrames: true,
13
+ js: ['dist/stub.js'],
14
+ runAt: 'document_start',
15
+ },
16
+ ])
17
+
18
+ // inject main agent when needed
19
+ const injectContentScript = (tabId: number) => {
20
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
21
+ chrome.scripting.executeScript({
22
+ target: {
23
+ tabId,
24
+ allFrames: false,
25
+ },
26
+ files: ['dist/content.js'],
27
+ })
28
+ }
29
+ const injectAgentScript = (tabId: number) => {
30
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
31
+ chrome.scripting.executeScript({
32
+ world: 'MAIN',
33
+ target: {
34
+ tabId,
35
+ allFrames: true,
36
+ },
37
+ files: ['dist/agent.js'],
38
+ })
39
+ }
40
+
41
+ // states
42
+ const tabMetaMap = Object.create(null) as Record<
43
+ number,
44
+ {
45
+ devTools: chrome.runtime.Port
46
+ contentScript?: chrome.runtime.Port
47
+ }
48
+ >
49
+ chrome.runtime.onConnect.addListener((port) => {
50
+ if (port.name === ConnectionSource.DevToolsPanel) {
51
+ newDevToolsConnection(port)
52
+ } else if (port.name === ConnectionSource.ContentScript) {
53
+ newContentScriptConnection(port)
54
+ }
55
+ })
56
+
57
+ // connections from DevTools
58
+ const newDevToolsConnection = (port: chrome.runtime.Port) => {
59
+ let tabId = 0
60
+ port.onMessage.addListener((message: PanelSendMessageMeta) => {
61
+ if (message.kind === '_init') {
62
+ if (tabId) delete tabMetaMap[tabId]
63
+ tabId = message.tabId
64
+ tabMetaMap[tabId] = { devTools: port }
65
+ injectContentScript(tabId)
66
+ } else if (message.kind !== '') {
67
+ const tabMeta = tabMetaMap[tabId]
68
+ if (!tabMeta) return
69
+ if (tabMeta.contentScript) {
70
+ tabMeta.contentScript.postMessage(message)
71
+ } else {
72
+ // eslint-disable-next-line no-console
73
+ console.warn('Failed to send message to agent since the agent is not connected')
74
+ }
75
+ }
76
+ })
77
+ port.onDisconnect.addListener((_port) => {
78
+ if (tabId) delete tabMetaMap[tabId]
79
+ })
80
+ }
81
+
82
+ // connections from content script
83
+ const newContentScriptConnection = (port: chrome.runtime.Port) => {
84
+ const tabId = port.sender?.tab?.id
85
+ if (tabId === undefined) return
86
+ const tabMeta = tabMetaMap[tabId]
87
+ if (!tabMeta) return
88
+ port.onMessage.addListener((message: AgentSendMessageMeta) => {
89
+ const tabMeta = tabMetaMap[tabId]
90
+ if (!tabMeta) return
91
+ if (message.kind === '_init') {
92
+ tabMeta.contentScript = port
93
+ tabMeta.devTools.postMessage({ kind: '_connected' })
94
+ } else {
95
+ tabMeta.devTools.postMessage(message)
96
+ }
97
+ })
98
+ port.onDisconnect.addListener((_port) => {
99
+ const tabMeta = tabMetaMap[tabId]
100
+ if (!tabMeta) return
101
+ tabMeta.contentScript = undefined
102
+ })
103
+ injectAgentScript(tabId)
104
+ }
105
+
106
+ // inject agent when reloaded
107
+ chrome.webNavigation.onDOMContentLoaded.addListener((ev) => {
108
+ const tabId = ev.tabId
109
+ if (ev.frameId !== 0) return
110
+ const tabMeta = tabMetaMap[tabId]
111
+ if (!tabMeta) return
112
+ injectContentScript(tabId)
113
+ })
114
+
115
+ // eslint-disable-next-line no-console
116
+ console.log('glass-easel DevTools extension started')
@@ -0,0 +1,53 @@
1
+ import { type protocol } from 'glass-easel-devtools-agent'
2
+ import { ConnectionSource, inFirefox } from '../utils'
3
+
4
+ declare function cloneInto<T>(x: T, target: Window): T
5
+
6
+ const prepareDataToAgent = <T>(data: T): T => {
7
+ if (inFirefox()) return cloneInto(data, window)
8
+ return data
9
+ }
10
+
11
+ // avoid double injection
12
+ const existingElements = document.querySelectorAll('glass-easel-devtools')
13
+ for (let i = 0; i < existingElements.length; i += 1) {
14
+ const hostElement = existingElements[i]
15
+ hostElement.parentNode?.removeChild(hostElement)
16
+ }
17
+
18
+ // create a host node
19
+ const hostElement = document.createElement('glass-easel-devtools')
20
+ const hostNodeStyle = `
21
+ display: none;
22
+ position: fixed;
23
+ left: 0;
24
+ top: 0;
25
+ right: 0;
26
+ bottom: 0;
27
+ `
28
+ hostElement.setAttribute('style', hostNodeStyle)
29
+ document.documentElement.appendChild(hostElement)
30
+
31
+ // messaging from background to agent
32
+ const background = chrome.runtime.connect({
33
+ name: ConnectionSource.ContentScript,
34
+ })
35
+ const sendHeartbeat = () => {
36
+ setTimeout(() => {
37
+ background.postMessage({ kind: '' })
38
+ sendHeartbeat()
39
+ }, 15000)
40
+ }
41
+ sendHeartbeat()
42
+ background.onMessage.addListener((message: protocol.AgentRecvMessage) => {
43
+ const ev = new CustomEvent('glass-easel-devtools-agent-recv', {
44
+ detail: prepareDataToAgent(message),
45
+ })
46
+ hostElement.dispatchEvent(ev)
47
+ })
48
+
49
+ // messaging from agent to background
50
+ hostElement.addEventListener('glass-easel-devtools-agent-send', (ev) => {
51
+ const { detail } = ev as CustomEvent<protocol.AgentSendMessage>
52
+ background.postMessage(detail)
53
+ })
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
6
+ </head>
7
+ <body>
8
+ <!-- empty -->
9
+ </body>
10
+ <script src="devtools.js"></script>
11
+ </html>
@@ -0,0 +1,13 @@
1
+ import { inFirefox } from '../utils'
2
+
3
+ chrome.devtools.panels.create(
4
+ 'glass-easel',
5
+ '../icons/glass-easel-grey-48.png',
6
+ inFirefox() ? 'panel.html' : 'dist/panel.html',
7
+ (_panel) => {
8
+ // empty
9
+ },
10
+ )
11
+
12
+ // eslint-disable-next-line no-console
13
+ console.log('glass-easel DevTools extension DevTools created')
@@ -0,0 +1,12 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width,initial-scale=1" />
6
+ <link rel="stylesheet" href="index.css" />
7
+ </head>
8
+ <body>
9
+ <!-- the entry component will be loaded here -->
10
+ </body>
11
+ <script src="panel.js"></script>
12
+ </html>
@@ -0,0 +1,53 @@
1
+ import * as glassEasel from 'glass-easel'
2
+ import {
3
+ startup,
4
+ restart,
5
+ type PanelRecvMessage,
6
+ type PanelSendMessage,
7
+ } from 'glass-easel-devtools-panel'
8
+ import { ConnectionSource } from '../utils'
9
+
10
+ export type PanelSendMessageMeta = PanelSendMessage | { kind: '_init'; tabId: number }
11
+ export type PanelRecvMessageMeta = PanelRecvMessage | { kind: '_connected' }
12
+
13
+ // build connection to main service
14
+ const background = chrome.runtime.connect({
15
+ name: ConnectionSource.DevToolsPanel,
16
+ })
17
+ const postToBackground = (msg: PanelSendMessageMeta) => {
18
+ background.postMessage(msg)
19
+ }
20
+ const sendHeartbeat = () => {
21
+ setTimeout(() => {
22
+ postToBackground({ kind: '' })
23
+ sendHeartbeat()
24
+ }, 15000)
25
+ }
26
+ sendHeartbeat()
27
+ background.onMessage.addListener((message: PanelRecvMessageMeta) => {
28
+ if (message.kind === '_connected') {
29
+ restart()
30
+ } else {
31
+ listener?.(message)
32
+ }
33
+ })
34
+ postToBackground({ kind: '_init', tabId: chrome.devtools.inspectedWindow.tabId })
35
+
36
+ // passing massage through channel
37
+ let listener: ((data: PanelRecvMessage) => void) | null = null
38
+ setTimeout(() => {
39
+ const hostContext = new glassEasel.CurrentWindowBackendContext()
40
+ const hostElement = document.body as unknown as glassEasel.GeneralBackendElement
41
+ const channel = {
42
+ send(data: PanelSendMessage) {
43
+ postToBackground(data)
44
+ },
45
+ recv(f: (data: PanelRecvMessage) => void) {
46
+ listener = f
47
+ },
48
+ }
49
+ startup(hostContext, hostElement, channel)
50
+ }, 0)
51
+
52
+ // eslint-disable-next-line no-console
53
+ console.log('glass-easel DevTools extension DevTools panel created')
@@ -0,0 +1,41 @@
1
+ /* eslint-disable class-methods-use-this */
2
+
3
+ import type * as glassEasel from 'glass-easel'
4
+ import { type DevTools, type DevToolsBridge, type InspectorDevTools } from '../utils'
5
+
6
+ let devToolsTarget: DevTools | null = null
7
+ const mountPoints = new Map<glassEasel.Element, glassEasel.MountPointEnv>()
8
+
9
+ class Inspector implements InspectorDevTools {
10
+ addMountPoint(elem: glassEasel.Element, env: glassEasel.MountPointEnv) {
11
+ mountPoints.set(elem, env)
12
+ if (devToolsTarget) devToolsTarget.inspector?.addMountPoint(elem, env)
13
+ }
14
+
15
+ removeMountPoint(elem: glassEasel.Element) {
16
+ mountPoints.delete(elem)
17
+ if (devToolsTarget) devToolsTarget.inspector?.removeMountPoint(elem)
18
+ }
19
+ }
20
+
21
+ const glassEaselDevTools = {
22
+ inspector: new Inspector(),
23
+ _devToolsConnect(target: DevTools) {
24
+ if (devToolsTarget) {
25
+ this._devToolsDisconnect()
26
+ }
27
+ devToolsTarget = target
28
+ mountPoints.forEach((env, elem) => target.inspector?.addMountPoint(elem, env))
29
+ },
30
+ _devToolsDisconnect() {
31
+ if (!devToolsTarget) return
32
+ const target = devToolsTarget
33
+ devToolsTarget = null
34
+ mountPoints.forEach((_, elem) => target.inspector?.removeMountPoint(elem))
35
+ },
36
+ }
37
+
38
+ const userGlobal = window as unknown as { __glassEaselDevTools__?: DevToolsBridge }
39
+ if (userGlobal.__glassEaselDevTools__ === undefined) {
40
+ userGlobal.__glassEaselDevTools__ = glassEaselDevTools
41
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,28 @@
1
+ import type * as glassEasel from 'glass-easel'
2
+
3
+ export const enum ExtensionEnv {
4
+ Chrome,
5
+ Firefox,
6
+ }
7
+
8
+ export const EXTENSION_ENV = navigator.userAgent.includes(' Firefox/')
9
+ ? ExtensionEnv.Firefox
10
+ : ExtensionEnv.Chrome
11
+
12
+ export const inChrome = (): boolean => EXTENSION_ENV === ExtensionEnv.Chrome
13
+
14
+ export const inFirefox = (): boolean => EXTENSION_ENV === ExtensionEnv.Firefox
15
+
16
+ export type DevTools = glassEasel.DevTools
17
+
18
+ export interface DevToolsBridge extends DevTools {
19
+ _devToolsConnect(target: DevTools): void
20
+ _devToolsDisconnect(): void
21
+ }
22
+
23
+ export type InspectorDevTools = glassEasel.InspectorDevTools
24
+
25
+ export const enum ConnectionSource {
26
+ DevToolsPanel = 'DevToolsPanel',
27
+ ContentScript = 'ContentScript',
28
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "extends": "../tsconfig.json",
3
+ "include": [
4
+ "src/**/*.ts"
5
+ ]
6
+ }
@@ -0,0 +1,76 @@
1
+ /* eslint-disable import/no-extraneous-dependencies */
2
+ /* eslint-disable @typescript-eslint/no-var-requires */
3
+
4
+ const path = require('path')
5
+ const CopyPlugin = require('copy-webpack-plugin')
6
+
7
+ const config = (input, output, copyRes) => {
8
+ const mainFields = {
9
+ mode: 'production',
10
+ entry: '[INPUT]',
11
+ output: {
12
+ filename: '[OUTPUT]',
13
+ path: path.join(__dirname, 'dist'),
14
+ module: false,
15
+ iife: true,
16
+ },
17
+ devtool: 'inline-source-map',
18
+ resolve: {
19
+ extensions: ['.ts', '.js'],
20
+ },
21
+ module: {
22
+ rules: [
23
+ {
24
+ test: /\.ts$/,
25
+ loader: 'ts-loader',
26
+ exclude: /node_modules/,
27
+ },
28
+ ],
29
+ },
30
+ watchOptions: {
31
+ aggregateTimeout: 500,
32
+ },
33
+ performance: {
34
+ hints: false,
35
+ maxEntrypointSize: 4 * 1024 * 1024,
36
+ maxAssetSize: 4 * 1024 * 1024,
37
+ },
38
+ plugins: [],
39
+ }
40
+ mainFields.entry = input
41
+ mainFields.output.filename = output
42
+ if (copyRes) {
43
+ mainFields.plugins.push(new CopyPlugin(copyRes))
44
+ }
45
+ return mainFields
46
+ }
47
+
48
+ module.exports = [
49
+ config('./src/agent/index.ts', 'agent.js'),
50
+ config('./src/background/index.ts', 'background.js'),
51
+ config('./src/content/index.ts', 'content.js'),
52
+ config('./src/devtools/index.ts', 'devtools.js', {
53
+ patterns: [
54
+ {
55
+ from: 'src/devtools/index.html',
56
+ to: 'devtools.html',
57
+ toType: 'file',
58
+ },
59
+ ],
60
+ }),
61
+ config('./src/panel/index.ts', 'panel.js', {
62
+ patterns: [
63
+ {
64
+ from: '**/*.+(jpg|jpeg|png|gif|css)',
65
+ to: '',
66
+ context: 'node_modules/glass-easel-devtools-panel/dist/',
67
+ },
68
+ {
69
+ from: 'src/panel/index.html',
70
+ to: 'panel.html',
71
+ toType: 'file',
72
+ },
73
+ ],
74
+ }),
75
+ config('./src/stub/index.ts', 'stub.js'),
76
+ ]