@linktr.ee/create-link-app 2.0.0-rc.1 โ 2.1.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 +8 -22
- package/dist/commands/build.js +80 -3
- package/dist/commands/create.js +9 -13
- package/dist/commands/deploy.js +16 -15
- package/dist/commands/dev.js +3 -0
- package/dist/commands/grant-access.js +3 -2
- package/dist/commands/login.js +8 -7
- package/dist/commands/logout.js +3 -2
- package/dist/commands/storybook.js +3 -1
- package/dist/commands/test-url-match-rules.js +4 -0
- package/dist/lib/create/create-project.js +2 -2
- package/dist/webpack/development.entry.js +19 -50
- package/dist/webpack/webpack.config.js +138 -3
- package/oclif.manifest.json +9 -27
- package/package.json +2 -1
- package/templates/common/url_match_rules.json +10 -0
- package/templates/react-ts/package.json +2 -3
- package/templates/react-ts/postcss.config.js +2 -2
- package/templates/react-ts/tailwind.config.js +2 -2
- package/dist/commands/generate-types.js +0 -102
- package/dist/webpack/simulator.js +0 -509
- package/templates/react/package.json +0 -16
- package/templates/react/src/index.jsx +0 -12
- package/templates/react/src/tailwind.css +0 -5
package/README.md
CHANGED
|
@@ -29,7 +29,6 @@ npx @linktr.ee/create-link-app --help
|
|
|
29
29
|
* [`create-link-app create NAME`](#create-link-app-create-name)
|
|
30
30
|
* [`create-link-app deploy`](#create-link-app-deploy)
|
|
31
31
|
* [`create-link-app dev`](#create-link-app-dev)
|
|
32
|
-
* [`create-link-app generate-types`](#create-link-app-generate-types)
|
|
33
32
|
* [`create-link-app grant-access LINK_APP_ID USERNAME`](#create-link-app-grant-access-link_app_id-username)
|
|
34
33
|
* [`create-link-app help [COMMAND]`](#create-link-app-help-command)
|
|
35
34
|
* [`create-link-app login`](#create-link-app-login)
|
|
@@ -43,10 +42,11 @@ Build Link App project to static assets used for production
|
|
|
43
42
|
|
|
44
43
|
```
|
|
45
44
|
USAGE
|
|
46
|
-
$ create-link-app build [--native]
|
|
45
|
+
$ create-link-app build [--native] [--profile]
|
|
47
46
|
|
|
48
47
|
FLAGS
|
|
49
|
-
--native
|
|
48
|
+
--native Build native components of a Link App ready for publishing to npm
|
|
49
|
+
--profile Enable detailed webpack profiling and bundle analysis
|
|
50
50
|
|
|
51
51
|
DESCRIPTION
|
|
52
52
|
Build Link App project to static assets used for production
|
|
@@ -58,16 +58,14 @@ Initialize a new Link App project
|
|
|
58
58
|
|
|
59
59
|
```
|
|
60
60
|
USAGE
|
|
61
|
-
$ create-link-app create NAME [-
|
|
61
|
+
$ create-link-app create NAME [-p <value>] [--storybook]
|
|
62
62
|
|
|
63
63
|
ARGUMENTS
|
|
64
64
|
NAME Name of the Link App
|
|
65
65
|
|
|
66
66
|
FLAGS
|
|
67
|
-
-p, --path=<value>
|
|
68
|
-
|
|
69
|
-
<options: react|react-ts>
|
|
70
|
-
--storybook With Storybook added to the project
|
|
67
|
+
-p, --path=<value> Path to create the project in
|
|
68
|
+
--storybook With Storybook added to the project
|
|
71
69
|
|
|
72
70
|
DESCRIPTION
|
|
73
71
|
Initialize a new Link App project
|
|
@@ -75,9 +73,9 @@ DESCRIPTION
|
|
|
75
73
|
EXAMPLES
|
|
76
74
|
$ create-link-app create my-link-app
|
|
77
75
|
|
|
78
|
-
$ create-link-app create my-link-app --template react
|
|
79
|
-
|
|
80
76
|
$ create-link-app create my-link-app --path my/custom/path
|
|
77
|
+
|
|
78
|
+
$ create-link-app create my-link-app --storybook
|
|
81
79
|
```
|
|
82
80
|
|
|
83
81
|
## `create-link-app deploy`
|
|
@@ -122,18 +120,6 @@ EXAMPLES
|
|
|
122
120
|
$ create-link-app dev --https
|
|
123
121
|
```
|
|
124
122
|
|
|
125
|
-
## `create-link-app generate-types`
|
|
126
|
-
|
|
127
|
-
Generate Typescript types from the schema definitions
|
|
128
|
-
|
|
129
|
-
```
|
|
130
|
-
USAGE
|
|
131
|
-
$ create-link-app generate-types
|
|
132
|
-
|
|
133
|
-
DESCRIPTION
|
|
134
|
-
Generate Typescript types from the schema definitions
|
|
135
|
-
```
|
|
136
|
-
|
|
137
123
|
## `create-link-app grant-access LINK_APP_ID USERNAME`
|
|
138
124
|
|
|
139
125
|
Grant access to other developers to push updates for your Link App
|
package/dist/commands/build.js
CHANGED
|
@@ -11,19 +11,30 @@ const child_process_1 = require("child_process");
|
|
|
11
11
|
class Build extends base_1.default {
|
|
12
12
|
async run() {
|
|
13
13
|
const { flags } = await this.parse(Build);
|
|
14
|
+
const allowAnyOrigin = flags['allow-any-origin'];
|
|
15
|
+
this.log('๐๏ธ Starting build process...');
|
|
14
16
|
if (flags['native']) {
|
|
17
|
+
this.log('๐ฑ Building native components...');
|
|
15
18
|
this.native();
|
|
16
19
|
}
|
|
17
20
|
else {
|
|
18
|
-
this.webpack({
|
|
19
|
-
allowAnyOrigin
|
|
21
|
+
await this.webpack({
|
|
22
|
+
allowAnyOrigin,
|
|
23
|
+
profile: flags.profile,
|
|
20
24
|
});
|
|
21
25
|
}
|
|
22
26
|
this.log('๐ Build complete');
|
|
23
27
|
}
|
|
24
28
|
async webpack(options) {
|
|
29
|
+
this.log('โฑ๏ธ Starting webpack build...');
|
|
30
|
+
const configStartTime = Date.now();
|
|
25
31
|
const config = await (0, webpack_config_1.default)('production', options);
|
|
32
|
+
const configEndTime = Date.now();
|
|
33
|
+
this.log(`๐ Webpack config generated in ${configEndTime - configStartTime}ms`);
|
|
34
|
+
const buildStartTime = Date.now();
|
|
26
35
|
(0, webpack_1.default)(config, (err, stats) => {
|
|
36
|
+
const buildEndTime = Date.now();
|
|
37
|
+
const totalTime = buildEndTime - buildStartTime;
|
|
27
38
|
if (err) {
|
|
28
39
|
this.error(err);
|
|
29
40
|
}
|
|
@@ -33,10 +44,73 @@ class Build extends base_1.default {
|
|
|
33
44
|
else if (stats?.hasWarnings()) {
|
|
34
45
|
this.warn(stats.toString());
|
|
35
46
|
}
|
|
47
|
+
else {
|
|
48
|
+
this.log(`โก Webpack build completed in ${totalTime}ms`);
|
|
49
|
+
// Display detailed timing information if available
|
|
50
|
+
if (stats?.compilation?.logging) {
|
|
51
|
+
const compilation = stats.compilation;
|
|
52
|
+
this.log('\n๐ Build Performance Summary:');
|
|
53
|
+
// Show module count and timing
|
|
54
|
+
const moduleCount = compilation.modules?.size || 0;
|
|
55
|
+
this.log(` ๐ฆ Modules processed: ${moduleCount}`);
|
|
56
|
+
// Show asset information
|
|
57
|
+
const assets = Object.keys(stats.compilation.assets || {});
|
|
58
|
+
if (assets.length > 0) {
|
|
59
|
+
this.log(` ๐๏ธ Assets generated: ${assets.length}`);
|
|
60
|
+
// Show largest assets
|
|
61
|
+
const assetSizes = assets
|
|
62
|
+
.map((name) => ({
|
|
63
|
+
name,
|
|
64
|
+
size: stats?.compilation.assets[name]?.size() || 0,
|
|
65
|
+
}))
|
|
66
|
+
.sort((a, b) => b.size - a.size)
|
|
67
|
+
.slice(0, 5);
|
|
68
|
+
this.log(' ๐ Largest assets:');
|
|
69
|
+
assetSizes.forEach((asset) => {
|
|
70
|
+
const sizeKB = (asset.size / 1024).toFixed(1);
|
|
71
|
+
this.log(` ${asset.name}: ${sizeKB}KB`);
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Show timing breakdown if stats are available
|
|
76
|
+
if (stats?.toString) {
|
|
77
|
+
const statsString = stats.toString({
|
|
78
|
+
colors: false,
|
|
79
|
+
modules: false,
|
|
80
|
+
chunks: false,
|
|
81
|
+
timings: true,
|
|
82
|
+
performance: true,
|
|
83
|
+
});
|
|
84
|
+
// Extract timing information from stats
|
|
85
|
+
const timingMatch = statsString.match(/Time: (\d+)ms/i);
|
|
86
|
+
if (timingMatch) {
|
|
87
|
+
this.log(` โฑ๏ธ Webpack internal timing: ${timingMatch[1]}ms`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
// Provide guidance for further analysis
|
|
91
|
+
if (options.profile) {
|
|
92
|
+
this.log('\n๐ Bundle analysis files generated:');
|
|
93
|
+
this.log(' ๐ Bundle report: dist/bundle-report.html');
|
|
94
|
+
this.log(' ๐ Webpack stats: dist/webpack-stats.json');
|
|
95
|
+
this.log(' ๐ก Open bundle-report.html in your browser to analyze bundle size');
|
|
96
|
+
this.log('\n๐ Detailed profiling enabled - check webpack output above for timing breakdown');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
36
99
|
});
|
|
37
100
|
}
|
|
38
101
|
async native() {
|
|
39
|
-
(0, child_process_1.spawn)('npx react-native-builder-bob build', { cwd: process.cwd(), shell: true, stdio: 'inherit' });
|
|
102
|
+
const nativeProcess = (0, child_process_1.spawn)('npx react-native-builder-bob build', { cwd: process.cwd(), shell: true, stdio: 'inherit' });
|
|
103
|
+
nativeProcess.on('error', (err) => {
|
|
104
|
+
this.error(`โ Failed to build native components: ${err.message}`);
|
|
105
|
+
});
|
|
106
|
+
nativeProcess.on('close', (code) => {
|
|
107
|
+
if (code === 0) {
|
|
108
|
+
this.log('โ
Native components built successfully');
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
this.error(`โ Native build failed with exit code ${code}`);
|
|
112
|
+
}
|
|
113
|
+
});
|
|
40
114
|
}
|
|
41
115
|
}
|
|
42
116
|
exports.default = Build;
|
|
@@ -49,4 +123,7 @@ Build.flags = {
|
|
|
49
123
|
native: core_1.Flags.boolean({
|
|
50
124
|
description: 'Build native components of a Link App ready for publishing to npm',
|
|
51
125
|
}),
|
|
126
|
+
profile: core_1.Flags.boolean({
|
|
127
|
+
description: 'Enable detailed webpack profiling and bundle analysis',
|
|
128
|
+
}),
|
|
52
129
|
};
|
package/dist/commands/create.js
CHANGED
|
@@ -13,29 +13,31 @@ class Create extends base_1.default {
|
|
|
13
13
|
async run() {
|
|
14
14
|
const { args, flags } = await this.parse(Create);
|
|
15
15
|
const targetDir = path_1.default.resolve(flags.path ?? process.cwd(), args.name);
|
|
16
|
-
this.log(`๐ Creating project in ${targetDir}...`);
|
|
16
|
+
this.log(`๐ Creating Link App project '${args.name}' in ${targetDir}...`);
|
|
17
17
|
if (flags.storybook) {
|
|
18
|
-
this.log('
|
|
18
|
+
this.log('๐จ Including Storybook in the project...');
|
|
19
19
|
}
|
|
20
|
-
await (0, create_project_1.default)(
|
|
20
|
+
await (0, create_project_1.default)(targetDir, { hasStorybook: flags.storybook });
|
|
21
21
|
const useYarn = (0, is_using_yarn_1.default)();
|
|
22
|
-
core_1.CliUx.ux.action.start('
|
|
22
|
+
core_1.CliUx.ux.action.start('๐ Installing dependencies');
|
|
23
23
|
(0, install_dependencies_1.default)(targetDir, useYarn);
|
|
24
24
|
core_1.CliUx.ux.action.stop();
|
|
25
25
|
const relativeProjectPath = path_1.default.relative(process.cwd(), targetDir);
|
|
26
|
-
this.log('
|
|
26
|
+
this.log('โ
Project created successfully!');
|
|
27
|
+
this.log('\n๐ To get started:');
|
|
27
28
|
this.log(` cd ${relativeProjectPath}`);
|
|
28
29
|
this.log(` yarn install | npm install`);
|
|
29
30
|
this.log(` yarn build | npm run build`);
|
|
30
|
-
this.log(` yarn dev | npm run dev
|
|
31
|
+
this.log(` yarn dev | npm run dev`);
|
|
32
|
+
this.log('\n๐ Happy coding!');
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
35
|
exports.default = Create;
|
|
34
36
|
Create.description = 'Initialize a new Link App project';
|
|
35
37
|
Create.examples = [
|
|
36
38
|
'$ create-link-app create my-link-app',
|
|
37
|
-
'$ create-link-app create my-link-app --template react',
|
|
38
39
|
'$ create-link-app create my-link-app --path my/custom/path',
|
|
40
|
+
'$ create-link-app create my-link-app --storybook',
|
|
39
41
|
];
|
|
40
42
|
Create.args = [
|
|
41
43
|
{
|
|
@@ -45,12 +47,6 @@ Create.args = [
|
|
|
45
47
|
},
|
|
46
48
|
];
|
|
47
49
|
Create.flags = {
|
|
48
|
-
template: core_1.Flags.string({
|
|
49
|
-
char: 't',
|
|
50
|
-
description: 'Template to use for the project',
|
|
51
|
-
options: ['react', 'react-ts'],
|
|
52
|
-
default: 'react-ts',
|
|
53
|
-
}),
|
|
54
50
|
path: core_1.Flags.string({
|
|
55
51
|
char: 'p',
|
|
56
52
|
description: 'Path to create the project in',
|
package/dist/commands/deploy.js
CHANGED
|
@@ -23,30 +23,31 @@ class Deploy extends base_1.default {
|
|
|
23
23
|
const isForceUpdate = !!flags['force-update'];
|
|
24
24
|
const isQa = !!flags.qa;
|
|
25
25
|
if (isForceUpdate) {
|
|
26
|
-
this.log('โ ๏ธ "--force-update" flag will impact the existing PUBLISHED Link Apps
|
|
26
|
+
this.log('โ ๏ธ "--force-update" flag will impact the existing PUBLISHED Link Apps.');
|
|
27
27
|
}
|
|
28
|
-
this.log(
|
|
28
|
+
this.log('๐ Starting deployment process...');
|
|
29
|
+
this.log('๐ฆ Packing project...');
|
|
29
30
|
const packedFiles = await (0, pack_project_1.default)(flags.path);
|
|
30
31
|
const linkAppName = this.getLinkAppId(flags.path);
|
|
31
32
|
const linkAppId = (0, slugify_1.default)(linkAppName, { lower: true });
|
|
32
|
-
this.log(`๐ Checking Link App by ID: [${linkAppId}]
|
|
33
|
+
this.log(`๐ Checking Link App by ID: [${linkAppId}]...`);
|
|
33
34
|
const linkAppExists = await (0, check_link_app_exists_1.default)(linkTypeServiceUrl, linkAppId, accessToken);
|
|
34
35
|
if (linkAppExists) {
|
|
35
|
-
this.log(
|
|
36
|
+
this.log(`โ
Link App [${linkAppId}] already exists, updating it...`);
|
|
36
37
|
}
|
|
37
38
|
else {
|
|
38
|
-
this.log(
|
|
39
|
+
this.log(`๐ Link App [${linkAppId}] does not exist, creating it...`);
|
|
39
40
|
}
|
|
40
41
|
if (packedFiles?.length) {
|
|
41
|
-
this.log('
|
|
42
|
+
this.log('๐ Ready to upload the following files:');
|
|
42
43
|
packedFiles.forEach((file) => {
|
|
43
|
-
this.log(`
|
|
44
|
+
this.log(` ๐ ${file}`);
|
|
44
45
|
});
|
|
45
46
|
}
|
|
46
47
|
if (!flags['skip-confirm']) {
|
|
47
48
|
const userConfirmDeploy = await core_1.CliUx.ux.prompt(`โ ${isQa ? '[QA]' : ''} Do you want to proceed? (Y/n)`, { required: false });
|
|
48
49
|
if (userConfirmDeploy?.toLowerCase() !== 'y') {
|
|
49
|
-
this.log('
|
|
50
|
+
this.log('โ Deployment aborted!');
|
|
50
51
|
return;
|
|
51
52
|
}
|
|
52
53
|
}
|
|
@@ -69,7 +70,7 @@ class Deploy extends base_1.default {
|
|
|
69
70
|
}
|
|
70
71
|
}
|
|
71
72
|
async processSuccess(flags, result) {
|
|
72
|
-
this.log(
|
|
73
|
+
this.log('\n๐ Deployment Response:');
|
|
73
74
|
this.log(JSON.stringify(result, null, 2));
|
|
74
75
|
const linkTypeId = result.linkType.linkTypeId;
|
|
75
76
|
const buildId = result.build?.id;
|
|
@@ -79,15 +80,15 @@ class Deploy extends base_1.default {
|
|
|
79
80
|
const linkAppCreateUrl = flags.qa
|
|
80
81
|
? `https://qa.linktr.ee/admin?action=create-link&linkType=${linkTypeId}`
|
|
81
82
|
: `https://linktr.ee/admin?action=create-link&linkType=${linkTypeId}`;
|
|
82
|
-
this.log(
|
|
83
|
+
this.log('\n๐ Deployment Summary:');
|
|
83
84
|
this.log(`โ
Link App successfully ${flags.update ? 'updated' : 'uploaded'}`);
|
|
84
|
-
this.log(
|
|
85
|
-
this.log(
|
|
86
|
-
this.log(
|
|
85
|
+
this.log(`๐ Link App ID: ${linkTypeId}`);
|
|
86
|
+
this.log(`๐ Status: ${result.linkType.status}`);
|
|
87
|
+
this.log(`๐ Updated: ${new Date(result.linkType.updated_timestamp * 1000).toLocaleString()}`);
|
|
87
88
|
if (buildId) {
|
|
88
|
-
this.log(
|
|
89
|
+
this.log(`๐ Check Link App build: ${linkAppBuildUrl}`);
|
|
89
90
|
}
|
|
90
|
-
this.log(
|
|
91
|
+
this.log(`๐ Create Link App: ${linkAppCreateUrl}`);
|
|
91
92
|
}
|
|
92
93
|
getLinkAppId(givenPath) {
|
|
93
94
|
const manifestPath = path_1.default.resolve(givenPath ?? process.cwd(), 'manifest.json');
|
package/dist/commands/dev.js
CHANGED
|
@@ -11,6 +11,7 @@ const webpack_config_1 = __importDefault(require("../webpack/webpack.config"));
|
|
|
11
11
|
class Dev extends base_1.default {
|
|
12
12
|
async run() {
|
|
13
13
|
const { flags } = await this.parse(Dev);
|
|
14
|
+
this.log('๐ Starting development server...');
|
|
14
15
|
const config = await (0, webpack_config_1.default)('development');
|
|
15
16
|
const devServer = new webpack_dev_server_1.default({
|
|
16
17
|
client: {
|
|
@@ -22,7 +23,9 @@ class Dev extends base_1.default {
|
|
|
22
23
|
port: flags.port,
|
|
23
24
|
allowedHosts: flags.allowedHosts,
|
|
24
25
|
}, (0, webpack_1.default)(config));
|
|
26
|
+
this.log(`๐ก Development server will start on ${flags.https ? 'https' : 'http'}://${flags.host}:${flags.port}`);
|
|
25
27
|
await devServer.start();
|
|
28
|
+
this.log('โ
Development server started successfully');
|
|
26
29
|
}
|
|
27
30
|
}
|
|
28
31
|
exports.default = Dev;
|
|
@@ -10,6 +10,7 @@ const access_token_1 = require("../lib/auth/access-token");
|
|
|
10
10
|
class GrantAccess extends base_1.default {
|
|
11
11
|
async run() {
|
|
12
12
|
const { args, flags } = await this.parse(GrantAccess);
|
|
13
|
+
this.log(`๐ Granting access to Link App '${args.link_app_id}' for user '${args.username}'...`);
|
|
13
14
|
const appConfig = await this.getAppConfig(flags.qa ? 'qa' : 'production');
|
|
14
15
|
const accessToken = (0, access_token_1.getAccessToken)(appConfig.auth.audience);
|
|
15
16
|
const url = `${flags.endpoint ?? appConfig.link_types_url}/link-types/${args.link_app_id}/maintainers`;
|
|
@@ -23,10 +24,10 @@ class GrantAccess extends base_1.default {
|
|
|
23
24
|
};
|
|
24
25
|
try {
|
|
25
26
|
await axios_1.default.post(url, maintainerDto, { headers });
|
|
26
|
-
this.log(
|
|
27
|
+
this.log(`โ
Successfully granted maintainer access to user '${args.username}'`);
|
|
27
28
|
}
|
|
28
29
|
catch (err) {
|
|
29
|
-
this.log(
|
|
30
|
+
this.log(`โ There was an error while attempting to grant access to user '${args.username}'`);
|
|
30
31
|
if (axios_1.default.isAxiosError(err)) {
|
|
31
32
|
if (err.response) {
|
|
32
33
|
this.error(JSON.stringify(err.response.data, null, 2));
|
package/dist/commands/login.js
CHANGED
|
@@ -10,25 +10,26 @@ const device_auth_1 = require("../lib/auth/device-auth");
|
|
|
10
10
|
class Login extends base_1.default {
|
|
11
11
|
async run() {
|
|
12
12
|
const { flags } = await this.parse(Login);
|
|
13
|
+
this.log('๐ Initiating login with Linktree credentials...');
|
|
13
14
|
const appConfig = await this.getAppConfig(flags.qa ? 'qa' : 'production');
|
|
14
15
|
const handle = await (0, device_auth_1.initDeviceAuthorization)(appConfig.auth);
|
|
15
16
|
const { expires_in, user_code, verification_uri_complete } = handle;
|
|
16
17
|
const expiryTime = expires_in % 60 === 0 ? `${expires_in / 60} minutes` : `${expires_in} seconds`;
|
|
17
|
-
this.log(
|
|
18
|
-
this.log(
|
|
19
|
-
this.log(
|
|
18
|
+
this.log(`๐ Please login via the opened web browser link. The browser window should display the following code: ${user_code}`);
|
|
19
|
+
this.log(`๐ If browser does not open automatically, please go to the following link: ${verification_uri_complete}`);
|
|
20
|
+
this.log(`โฐ This link expires in ${expiryTime}. Press Ctrl-C to abort.`);
|
|
20
21
|
core_1.CliUx.ux.open(verification_uri_complete);
|
|
21
|
-
core_1.CliUx.ux.action.start('Waiting for user to authorize device from browser');
|
|
22
|
+
core_1.CliUx.ux.action.start('โณ Waiting for user to authorize device from browser');
|
|
22
23
|
const token = await (0, device_auth_1.pollAccessToken)(handle);
|
|
23
24
|
core_1.CliUx.ux.action.stop();
|
|
24
25
|
if ((0, access_token_1.isTokenValid)(token)) {
|
|
25
26
|
(0, access_token_1.saveAccessToken)(token.access_token);
|
|
26
27
|
if (flags.qa) {
|
|
27
|
-
this.log('TOKEN: ', token.access_token);
|
|
28
|
+
this.log('๐ TOKEN: ', token.access_token);
|
|
28
29
|
}
|
|
29
|
-
this.log('
|
|
30
|
+
this.log('โ
Device has been authorized. Login successful.');
|
|
30
31
|
if (token.expires_at) {
|
|
31
|
-
this.log(
|
|
32
|
+
this.log(`โฐ Login will expire at ${new Date(token.expires_at * 1000).toString()}`);
|
|
32
33
|
}
|
|
33
34
|
}
|
|
34
35
|
}
|
package/dist/commands/logout.js
CHANGED
|
@@ -9,6 +9,7 @@ const access_token_1 = require("../lib/auth/access-token");
|
|
|
9
9
|
class Logout extends base_1.default {
|
|
10
10
|
async run() {
|
|
11
11
|
const { flags } = await this.parse(Logout);
|
|
12
|
+
this.log('๐ Logging out and clearing browser session...');
|
|
12
13
|
const appConfig = await this.getAppConfig(flags.qa ? 'qa' : 'production');
|
|
13
14
|
const { auth, logout_redirect_url } = appConfig;
|
|
14
15
|
const { domain, client_id, audience } = auth;
|
|
@@ -16,8 +17,8 @@ class Logout extends base_1.default {
|
|
|
16
17
|
// clear Auth0 session cookies so user gets prompted to enter credentials again for next login
|
|
17
18
|
const url = `${domain}/v2/logout?client_id=${client_id}&returnTo=${logout_redirect_url}`;
|
|
18
19
|
core_1.CliUx.ux.open(url);
|
|
19
|
-
this.log('Logout successful.');
|
|
20
|
-
this.log(
|
|
20
|
+
this.log('โ
Logout successful.');
|
|
21
|
+
this.log(`๐ If browser did not open automatically, please go to the following link to logout from the browser: ${url}`);
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
exports.default = Logout;
|
|
@@ -11,10 +11,12 @@ class Storybook extends base_1.default {
|
|
|
11
11
|
async run() {
|
|
12
12
|
const configPath = path_1.default.resolve(__dirname, '..', 'storybook');
|
|
13
13
|
const { flags } = await this.parse(Storybook);
|
|
14
|
+
this.log(`๐ Starting Storybook development server on port ${flags.port}...`);
|
|
14
15
|
const storybook = (0, child_process_1.spawn)(`start-storybook -p ${flags.port} -c ${configPath}`, { shell: true, stdio: 'inherit' });
|
|
15
16
|
storybook.on('error', (err) => {
|
|
16
|
-
|
|
17
|
+
this.error(`โ Failed to start Storybook: ${err.message}`);
|
|
17
18
|
});
|
|
19
|
+
this.log('โ
Storybook server started successfully');
|
|
18
20
|
}
|
|
19
21
|
}
|
|
20
22
|
exports.default = Storybook;
|
|
@@ -12,6 +12,7 @@ const test_url_match_rules_1 = __importDefault(require("../lib/deploy/test-url-m
|
|
|
12
12
|
class TestUrlMatchRules extends base_1.default {
|
|
13
13
|
async run() {
|
|
14
14
|
const { args, flags } = await this.parse(TestUrlMatchRules);
|
|
15
|
+
this.log(`๐งช Testing URL match rules for: ${args.url}`);
|
|
15
16
|
const appConfig = await this.getAppConfig(flags.qa ? 'qa' : 'production');
|
|
16
17
|
const linkTypeServiceUrl = `${flags.endpoint ?? appConfig.link_types_url}/link-types`;
|
|
17
18
|
const accessToken = (0, access_token_1.getAccessToken)(appConfig.auth.audience);
|
|
@@ -24,6 +25,7 @@ class TestUrlMatchRules extends base_1.default {
|
|
|
24
25
|
if (!fs_1.default.existsSync(urlMatchRulesPath)) {
|
|
25
26
|
this.error('โ url_match_rules.json not found', { exit: 1 });
|
|
26
27
|
}
|
|
28
|
+
this.log('๐ Loading URL match rules from url_match_rules.json...');
|
|
27
29
|
const urlMatchRulesString = fs_1.default.readFileSync(urlMatchRulesPath, 'utf8');
|
|
28
30
|
if (!urlMatchRulesString) {
|
|
29
31
|
this.error('โ url_match_rules.json is empty');
|
|
@@ -31,11 +33,13 @@ class TestUrlMatchRules extends base_1.default {
|
|
|
31
33
|
let urlMatchRules = null;
|
|
32
34
|
try {
|
|
33
35
|
urlMatchRules = JSON.parse(urlMatchRulesString);
|
|
36
|
+
this.log('โ
URL match rules loaded successfully');
|
|
34
37
|
}
|
|
35
38
|
catch (error) {
|
|
36
39
|
this.error('โ url_match_rules.json is invalid');
|
|
37
40
|
}
|
|
38
41
|
try {
|
|
42
|
+
this.log('๐ Testing URL against match rules...');
|
|
39
43
|
const result = await (0, test_url_match_rules_1.default)(linkTypeServiceUrl, url, urlMatchRules, accessToken);
|
|
40
44
|
const success = result.success;
|
|
41
45
|
this.log(`${success ? '๐' : 'โ'} Test URL Match Rules Result:`);
|
|
@@ -8,8 +8,8 @@ const path_1 = __importDefault(require("path"));
|
|
|
8
8
|
const project_name_to_title_1 = __importDefault(require("./project-name-to-title"));
|
|
9
9
|
// templates path relative to this module
|
|
10
10
|
const BaseTemplatesDir = path_1.default.resolve(__dirname, '..', '..', '..', 'templates');
|
|
11
|
-
const createProject = async (
|
|
12
|
-
const templateDir = path_1.default.resolve(BaseTemplatesDir,
|
|
11
|
+
const createProject = async (targetDir, options) => {
|
|
12
|
+
const templateDir = path_1.default.resolve(BaseTemplatesDir, 'react-ts');
|
|
13
13
|
const templateCommonDir = path_1.default.resolve(BaseTemplatesDir, 'common');
|
|
14
14
|
await fs_extra_1.default.copy(templateDir, targetDir);
|
|
15
15
|
await fs_extra_1.default.copy(templateCommonDir, targetDir);
|
|
@@ -28,64 +28,33 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
29
|
const react_1 = __importStar(require("react"));
|
|
30
30
|
const client_1 = require("react-dom/client");
|
|
31
|
-
const simulator_1 = __importDefault(require("./simulator"));
|
|
32
31
|
const extension_1 = __importDefault(require("@linktr.ee/extension"));
|
|
33
32
|
const extension_dev_data_1 = __importDefault(require("@linktr.ee/extension-dev-data"));
|
|
34
33
|
const container = document.getElementById('root');
|
|
35
34
|
const root = (0, client_1.createRoot)(container);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const [extensionData, setExtensionData] = (0, react_1.useState)(extension_dev_data_1.default);
|
|
35
|
+
const App = () => {
|
|
36
|
+
// TODO: refactor common postMessage to parent in both entry points
|
|
39
37
|
(0, react_1.useEffect)(() => {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
window.parent.postMessage(loaded, '*');
|
|
47
|
-
const initialHeight = Math.ceil(wrapperRef.current?.getBoundingClientRect().height || document.documentElement.scrollHeight || container.clientHeight);
|
|
48
|
-
postReady(initialHeight);
|
|
49
|
-
const ro = new window.ResizeObserver((entries) => {
|
|
50
|
-
const entry = entries[0];
|
|
51
|
-
const h = Math.ceil(entry?.contentRect?.height || wrapperRef.current?.getBoundingClientRect().height || 0);
|
|
52
|
-
if (h)
|
|
53
|
-
postReady(h);
|
|
54
|
-
});
|
|
55
|
-
if (wrapperRef.current)
|
|
56
|
-
ro.observe(wrapperRef.current);
|
|
57
|
-
const onMessage = (event) => {
|
|
58
|
-
if (event.data?.type === 'extension-data') {
|
|
59
|
-
const data = event.data?.data || {};
|
|
60
|
-
// eslint-disable-next-line no-console
|
|
61
|
-
console.info('[Extension] received extension-data', data);
|
|
62
|
-
setExtensionData((prev) => ({ ...prev, ...data }));
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
// forward interaction events to the parent (parity with production)
|
|
66
|
-
const onInteraction = (event) => window.parent.postMessage({ type: 'interaction-event', data: event.detail }, '*');
|
|
67
|
-
document.addEventListener('interaction-event', onInteraction);
|
|
68
|
-
window.addEventListener('message', onMessage);
|
|
69
|
-
return () => {
|
|
70
|
-
if (wrapperRef.current) {
|
|
71
|
-
try {
|
|
72
|
-
ro.unobserve(wrapperRef.current);
|
|
73
|
-
}
|
|
74
|
-
catch (e) {
|
|
75
|
-
// ignore unobserve errors in dev simulator
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
document.removeEventListener('interaction-event', onInteraction);
|
|
79
|
-
window.removeEventListener('message', onMessage);
|
|
38
|
+
const message = {
|
|
39
|
+
type: 'extension-ready',
|
|
40
|
+
data: {
|
|
41
|
+
ready: true,
|
|
42
|
+
height: container.clientHeight,
|
|
43
|
+
},
|
|
80
44
|
};
|
|
45
|
+
window.parent.postMessage(message, '*');
|
|
81
46
|
}, []);
|
|
82
|
-
return (react_1.default.createElement("div", { ref: wrapperRef, style: { display: 'block' } },
|
|
83
|
-
react_1.default.createElement(extension_1.default, { ...extensionData })));
|
|
84
|
-
}
|
|
85
|
-
const App = () => {
|
|
86
47
|
if (window.location.search === '?embed') {
|
|
87
|
-
return react_1.default.createElement(
|
|
48
|
+
return react_1.default.createElement(extension_1.default, { ...extension_dev_data_1.default });
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
return (react_1.default.createElement("iframe", { src: `${window.location}?embed`, sandbox: ['allow-scripts', 'allow-same-origin', 'allow-popups', 'allow-popups-to-escape-sandbox', 'allow-forms'].join(' '), scrolling: "no", frameBorder: 0, style: {
|
|
52
|
+
display: 'block',
|
|
53
|
+
margin: '0 auto',
|
|
54
|
+
width: '1px',
|
|
55
|
+
minWidth: '680px',
|
|
56
|
+
height: '100vh',
|
|
57
|
+
} }));
|
|
88
58
|
}
|
|
89
|
-
return react_1.default.createElement(simulator_1.default, null);
|
|
90
59
|
};
|
|
91
60
|
root.render(react_1.default.createElement(App, null));
|