@ichicraft/widgets-widget-base 1.18.0 → 1.20.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/README.md +463 -450
- package/lib/dev/createWidgetDevServerConfig.d.ts +31 -0
- package/lib/dev/createWidgetDevServerConfig.js +92 -0
- package/lib/dev/findAvailablePort.d.ts +5 -0
- package/lib/dev/findAvailablePort.js +55 -0
- package/lib/dev/index.d.ts +2 -0
- package/lib/dev/index.js +8 -0
- package/lib/types/index.d.ts +17 -15
- package/lib/types/index.js +1 -13
- package/package.json +21 -5
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Configuration as DevServerConfiguration } from 'webpack-dev-server';
|
|
2
|
+
import type { WidgetDebugServeConfig } from '../types';
|
|
3
|
+
/** Port the first widget dev server claims. Later ones move up from here. */
|
|
4
|
+
export declare const DEFAULT_WIDGET_DEBUG_PORT = 8080;
|
|
5
|
+
export interface CreateWidgetDevServerConfigOptions {
|
|
6
|
+
/** Root folder of the widget project, normally `__dirname` from webpack.dev.ts. */
|
|
7
|
+
projectPath: string;
|
|
8
|
+
/** The widget's `config/serve.config.ts` export. */
|
|
9
|
+
serveConfig: WidgetDebugServeConfig;
|
|
10
|
+
}
|
|
11
|
+
export interface WidgetDevServerConfigResult {
|
|
12
|
+
/** The port the dev server will listen on. */
|
|
13
|
+
port: number;
|
|
14
|
+
devServer: DevServerConfiguration;
|
|
15
|
+
/**
|
|
16
|
+
* Value for the css-loader `localIdentName` option. Keyed by port so two widgets served at
|
|
17
|
+
* the same time -- and the CDN build of the same widget -- never share a class name.
|
|
18
|
+
*/
|
|
19
|
+
cssModuleLocalIdentName: string;
|
|
20
|
+
/** Where the board fetches this widget's manifest from. */
|
|
21
|
+
manifestUrl: string;
|
|
22
|
+
/** The board url that gets opened, or null when no debug page is configured. */
|
|
23
|
+
debugPageUrl: string | null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Builds the dev server configuration for debugging a widget against a live widget board.
|
|
27
|
+
*
|
|
28
|
+
* The port is resolved at config time rather than hardcoded, so the manifest url handed to the
|
|
29
|
+
* board always matches the port the server actually binds to.
|
|
30
|
+
*/
|
|
31
|
+
export declare function createWidgetDevServerConfig(options: CreateWidgetDevServerConfigOptions): Promise<WidgetDevServerConfigResult>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.createWidgetDevServerConfig = exports.DEFAULT_WIDGET_DEBUG_PORT = void 0;
|
|
16
|
+
const fs_1 = __importDefault(require("fs"));
|
|
17
|
+
const os_1 = __importDefault(require("os"));
|
|
18
|
+
const path_1 = __importDefault(require("path"));
|
|
19
|
+
const url_1 = require("url");
|
|
20
|
+
const findAvailablePort_1 = require("./findAvailablePort");
|
|
21
|
+
/** Port the first widget dev server claims. Later ones move up from here. */
|
|
22
|
+
exports.DEFAULT_WIDGET_DEBUG_PORT = 8080;
|
|
23
|
+
/**
|
|
24
|
+
* Builds the dev server configuration for debugging a widget against a live widget board.
|
|
25
|
+
*
|
|
26
|
+
* The port is resolved at config time rather than hardcoded, so the manifest url handed to the
|
|
27
|
+
* board always matches the port the server actually binds to.
|
|
28
|
+
*/
|
|
29
|
+
function createWidgetDevServerConfig(options) {
|
|
30
|
+
var _a;
|
|
31
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
32
|
+
const { projectPath, serveConfig } = options;
|
|
33
|
+
const port = (_a = serveConfig.debugPort) !== null && _a !== void 0 ? _a : (yield (0, findAvailablePort_1.findAvailablePort)(exports.DEFAULT_WIDGET_DEBUG_PORT));
|
|
34
|
+
const manifestUrl = `https://localhost:${port}/manifest.json`;
|
|
35
|
+
let debugPageUrl = null;
|
|
36
|
+
if (serveConfig.widgetsDebugPageUrl && serveConfig.openBrowser !== false) {
|
|
37
|
+
const serveUrl = new url_1.URL(serveConfig.widgetsDebugPageUrl);
|
|
38
|
+
serveUrl.searchParams.append('debugWidgetManifest', manifestUrl);
|
|
39
|
+
debugPageUrl = serveUrl.href;
|
|
40
|
+
}
|
|
41
|
+
return {
|
|
42
|
+
port,
|
|
43
|
+
manifestUrl,
|
|
44
|
+
debugPageUrl,
|
|
45
|
+
cssModuleLocalIdentName: `[local]_${port}`,
|
|
46
|
+
devServer: {
|
|
47
|
+
static: path_1.default.join(projectPath, './dist'),
|
|
48
|
+
port,
|
|
49
|
+
host: 'localhost',
|
|
50
|
+
allowedHosts: 'all',
|
|
51
|
+
headers: {
|
|
52
|
+
'Access-Control-Allow-Origin': '*',
|
|
53
|
+
},
|
|
54
|
+
client: {
|
|
55
|
+
overlay: false,
|
|
56
|
+
},
|
|
57
|
+
server: {
|
|
58
|
+
type: 'https',
|
|
59
|
+
options: resolveDevCertificate(),
|
|
60
|
+
},
|
|
61
|
+
open: debugPageUrl
|
|
62
|
+
? {
|
|
63
|
+
target: [debugPageUrl],
|
|
64
|
+
app: {
|
|
65
|
+
name: 'google-chrome',
|
|
66
|
+
arguments: ['--incognito', '--new-window'],
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
: false,
|
|
70
|
+
devMiddleware: {
|
|
71
|
+
writeToDisk: true,
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
exports.createWidgetDevServerConfig = createWidgetDevServerConfig;
|
|
78
|
+
/**
|
|
79
|
+
* Uses the SPFx localhost certificate that was trusted with `gulp trust-dev-cert`,
|
|
80
|
+
* falling back to the rushstack certificate on newer toolchains.
|
|
81
|
+
*/
|
|
82
|
+
function resolveDevCertificate() {
|
|
83
|
+
const gcbCert = path_1.default.join(os_1.default.homedir(), '.gcb-serve-data', 'gcb-serve.cer');
|
|
84
|
+
const gcbKey = path_1.default.join(os_1.default.homedir(), '.gcb-serve-data', 'gcb-serve.key');
|
|
85
|
+
if (fs_1.default.existsSync(gcbCert) && fs_1.default.existsSync(gcbKey)) {
|
|
86
|
+
return { cert: fs_1.default.readFileSync(gcbCert), key: fs_1.default.readFileSync(gcbKey) };
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
cert: fs_1.default.readFileSync(path_1.default.join(os_1.default.homedir(), '.rushstack', 'rushstack-serve.pem')),
|
|
90
|
+
key: fs_1.default.readFileSync(path_1.default.join(os_1.default.homedir(), '.rushstack', 'rushstack-serve.key')),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolves the first free TCP port at or above `startPort`, so several widget dev servers
|
|
3
|
+
* can run side by side without anyone having to hand out port numbers.
|
|
4
|
+
*/
|
|
5
|
+
export declare function findAvailablePort(startPort: number, attempts?: number): Promise<number>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
12
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.findAvailablePort = void 0;
|
|
16
|
+
const net_1 = __importDefault(require("net"));
|
|
17
|
+
/** Both loopback stacks are checked: a dev server on one of them still owns the port. */
|
|
18
|
+
const LOOPBACK_HOSTS = ['127.0.0.1', '::1'];
|
|
19
|
+
/**
|
|
20
|
+
* Resolves the first free TCP port at or above `startPort`, so several widget dev servers
|
|
21
|
+
* can run side by side without anyone having to hand out port numbers.
|
|
22
|
+
*/
|
|
23
|
+
function findAvailablePort(startPort, attempts = 50) {
|
|
24
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
25
|
+
for (let port = startPort; port < startPort + attempts; port++) {
|
|
26
|
+
if (yield isPortAvailable(port)) {
|
|
27
|
+
return port;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
throw new Error(`[widgets-widget-base] No free port found between ${startPort} and ${startPort + attempts - 1}. Stop a running dev server or set "debugPort" in config/serve.config.ts.`);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
exports.findAvailablePort = findAvailablePort;
|
|
34
|
+
function isPortAvailable(port) {
|
|
35
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
36
|
+
for (const host of LOOPBACK_HOSTS) {
|
|
37
|
+
if (!(yield isPortAvailableOnHost(port, host))) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return true;
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function isPortAvailableOnHost(port, host) {
|
|
45
|
+
return new Promise((resolve) => {
|
|
46
|
+
const server = net_1.default.createServer();
|
|
47
|
+
server.once('error', (err) => {
|
|
48
|
+
// Anything other than "taken" means we can't bind this stack at all (no IPv6, for
|
|
49
|
+
// example), which says nothing about whether the port is free.
|
|
50
|
+
resolve(err.code !== 'EADDRINUSE');
|
|
51
|
+
});
|
|
52
|
+
server.once('listening', () => server.close(() => resolve(true)));
|
|
53
|
+
server.listen(port, host);
|
|
54
|
+
});
|
|
55
|
+
}
|
package/lib/dev/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findAvailablePort = exports.DEFAULT_WIDGET_DEBUG_PORT = exports.createWidgetDevServerConfig = void 0;
|
|
4
|
+
var createWidgetDevServerConfig_1 = require("./createWidgetDevServerConfig");
|
|
5
|
+
Object.defineProperty(exports, "createWidgetDevServerConfig", { enumerable: true, get: function () { return createWidgetDevServerConfig_1.createWidgetDevServerConfig; } });
|
|
6
|
+
Object.defineProperty(exports, "DEFAULT_WIDGET_DEBUG_PORT", { enumerable: true, get: function () { return createWidgetDevServerConfig_1.DEFAULT_WIDGET_DEBUG_PORT; } });
|
|
7
|
+
var findAvailablePort_1 = require("./findAvailablePort");
|
|
8
|
+
Object.defineProperty(exports, "findAvailablePort", { enumerable: true, get: function () { return findAvailablePort_1.findAvailablePort; } });
|
package/lib/types/index.d.ts
CHANGED
|
@@ -226,6 +226,14 @@ export interface WidgetContext {
|
|
|
226
226
|
* Provide the id (GUID) of the Bloom Group.
|
|
227
227
|
*/
|
|
228
228
|
isCurrentUserMemberOfBloomGroup?: (groupId: string) => Promise<boolean>;
|
|
229
|
+
/**
|
|
230
|
+
* Provides the tenant's Bloom Group definitions, e.g. so pickers can offer them.
|
|
231
|
+
* Untyped to avoid a cross-package dependency (like `theme`); items carry the
|
|
232
|
+
* Bloom Group shape as defined by the host.
|
|
233
|
+
*/
|
|
234
|
+
bloomGroups?: {
|
|
235
|
+
fetchGroups(): Promise<any[]>;
|
|
236
|
+
};
|
|
229
237
|
/**
|
|
230
238
|
* Generates a hash of all combined SP Groups of the current user. This can be used as
|
|
231
239
|
* cache invalidator to detect changes.
|
|
@@ -697,17 +705,6 @@ export interface WidgetManifestConfig {
|
|
|
697
705
|
}[];
|
|
698
706
|
};
|
|
699
707
|
}
|
|
700
|
-
export declare enum DebugComponentType {
|
|
701
|
-
/**
|
|
702
|
-
* This is the default component type and just opens the default widget board.
|
|
703
|
-
*/
|
|
704
|
-
Default = "Default",
|
|
705
|
-
/**
|
|
706
|
-
* This automatically opens the widget board administration panel and opens the widget admin config
|
|
707
|
-
* dialog of the widget you're working on
|
|
708
|
-
*/
|
|
709
|
-
AdminConfig = "AdminConfig"
|
|
710
|
-
}
|
|
711
708
|
/**
|
|
712
709
|
* The widget debug serve config contains settings for debugging a widget in development
|
|
713
710
|
*/
|
|
@@ -718,11 +715,16 @@ export interface WidgetDebugServeConfig {
|
|
|
718
715
|
*/
|
|
719
716
|
widgetsDebugPageUrl: string;
|
|
720
717
|
/**
|
|
721
|
-
*
|
|
722
|
-
*
|
|
723
|
-
|
|
718
|
+
* Pin the dev server to a specific port. When omitted, the first free port from 8080 upwards
|
|
719
|
+
* is used, so several widgets can be debugged side by side without configuration.
|
|
720
|
+
*/
|
|
721
|
+
debugPort?: number;
|
|
722
|
+
/**
|
|
723
|
+
* Open the widget board in a browser when the dev server starts. Defaults to true.
|
|
724
|
+
* Set this to false for the second and further widgets in a multi-widget debug session:
|
|
725
|
+
* they only need to serve their bundle and are registered from the board that is already open.
|
|
724
726
|
*/
|
|
725
|
-
|
|
727
|
+
openBrowser?: boolean;
|
|
726
728
|
}
|
|
727
729
|
/**
|
|
728
730
|
* Tells the severity of the command bar item, resulting in
|
package/lib/types/index.js
CHANGED
|
@@ -1,18 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CommandBarItemType = exports.CustomCommandBarItemSeverity =
|
|
4
|
-
var DebugComponentType;
|
|
5
|
-
(function (DebugComponentType) {
|
|
6
|
-
/**
|
|
7
|
-
* This is the default component type and just opens the default widget board.
|
|
8
|
-
*/
|
|
9
|
-
DebugComponentType["Default"] = "Default";
|
|
10
|
-
/**
|
|
11
|
-
* This automatically opens the widget board administration panel and opens the widget admin config
|
|
12
|
-
* dialog of the widget you're working on
|
|
13
|
-
*/
|
|
14
|
-
DebugComponentType["AdminConfig"] = "AdminConfig";
|
|
15
|
-
})(DebugComponentType || (exports.DebugComponentType = DebugComponentType = {}));
|
|
3
|
+
exports.CommandBarItemType = exports.CustomCommandBarItemSeverity = void 0;
|
|
16
4
|
/**
|
|
17
5
|
* Tells the severity of the command bar item, resulting in
|
|
18
6
|
* distinguishable presentation of the item
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ichicraft/widgets-widget-base",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.20.0",
|
|
4
4
|
"description": "Part of the Widget Development Kit for building widgets for Bloom Intranet",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"build": "tsc --build --force --verbose",
|
|
9
|
-
"build:watch": "tsc
|
|
8
|
+
"build": "tsc --build --force --verbose tsconfig.json tsconfig.dev.json",
|
|
9
|
+
"build:watch": "tsc --build --force --verbose --watch tsconfig.json tsconfig.dev.json",
|
|
10
10
|
"clean": "rimraf lib",
|
|
11
11
|
"pretty": "prettier --write \"./**/*.{js,jsx,mjs,cjs,ts,tsx,json}\"",
|
|
12
12
|
"prepublishOnly": "npm run clean && npm run build"
|
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"author": "Ichicraft",
|
|
20
20
|
"license": "UNLICENSED",
|
|
21
21
|
"files": [
|
|
22
|
-
"lib/**/*"
|
|
22
|
+
"lib/**/*",
|
|
23
|
+
"!lib/**/*.tsbuildinfo"
|
|
23
24
|
],
|
|
24
25
|
"overrides": {
|
|
25
26
|
"requirejs": "2.3.7",
|
|
@@ -34,12 +35,27 @@
|
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@ianvs/prettier-plugin-sort-imports": "4.4.1",
|
|
38
|
+
"@types/node": "^18.19.0",
|
|
37
39
|
"eslint": "8.57.0",
|
|
38
40
|
"eslint-config-prettier": "9.1.0",
|
|
39
41
|
"eslint-plugin-prettier": "5.2.1",
|
|
40
42
|
"eslint-plugin-react": "7.35.2",
|
|
41
43
|
"eslint-plugin-react-hooks": "4.6.2",
|
|
42
44
|
"prettier": "3.3.3",
|
|
43
|
-
"typescript": "~5.3.3"
|
|
45
|
+
"typescript": "~5.3.3",
|
|
46
|
+
"webpack": "^5.109.2",
|
|
47
|
+
"webpack-dev-server": "^5.2.6"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"webpack": "^5.0.0",
|
|
51
|
+
"webpack-dev-server": "^5.0.0"
|
|
52
|
+
},
|
|
53
|
+
"peerDependenciesMeta": {
|
|
54
|
+
"webpack": {
|
|
55
|
+
"optional": true
|
|
56
|
+
},
|
|
57
|
+
"webpack-dev-server": {
|
|
58
|
+
"optional": true
|
|
59
|
+
}
|
|
44
60
|
}
|
|
45
61
|
}
|