@linktr.ee/create-link-app 1.4.0 → 1.5.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 CHANGED
@@ -57,7 +57,7 @@ Initialize a new Link App project
57
57
 
58
58
  ```
59
59
  USAGE
60
- $ create-link-app create NAME [-t react|react-ts] [-p <value>]
60
+ $ create-link-app create NAME [-t react|react-ts] [-p <value>] [--storybook]
61
61
 
62
62
  ARGUMENTS
63
63
  NAME Name of the Link App
@@ -66,6 +66,7 @@ FLAGS
66
66
  -p, --path=<value> Path to create the project in
67
67
  -t, --template=<option> [default: react-ts] Template to use for the project
68
68
  <options: react|react-ts>
69
+ --storybook With Storybook added to the project
69
70
 
70
71
  DESCRIPTION
71
72
  Initialize a new Link App project
@@ -14,13 +14,16 @@ class Create extends base_1.default {
14
14
  const { args, flags } = await this.parse(Create);
15
15
  const targetDir = path_1.default.resolve(flags.path ?? process.cwd(), args.name);
16
16
  this.log(`🔗 Creating project in ${targetDir}...`);
17
- await (0, create_project_1.default)(flags.template, targetDir);
17
+ if (flags.storybook) {
18
+ this.log(' 🎨 Including Storybook in the project...');
19
+ }
20
+ await (0, create_project_1.default)(flags.template, targetDir, { hasStorybook: flags.storybook });
18
21
  const useYarn = (0, is_using_yarn_1.default)();
19
- core_1.CliUx.ux.action.start('🚚 Installing dependencies');
22
+ core_1.CliUx.ux.action.start(' 🚚 Installing dependencies');
20
23
  (0, install_dependencies_1.default)(targetDir, useYarn);
21
24
  core_1.CliUx.ux.action.stop();
22
25
  const relativeProjectPath = path_1.default.relative(process.cwd(), targetDir);
23
- this.log('✅ Project created, to get started:\n');
26
+ this.log('\n✅ Project created, to get started:');
24
27
  this.log(` cd ${relativeProjectPath}`);
25
28
  this.log(` yarn install | npm install`);
26
29
  this.log(` yarn dev | npm run dev\n`);
@@ -51,4 +54,8 @@ Create.flags = {
51
54
  char: 'p',
52
55
  description: 'Path to create the project in',
53
56
  }),
57
+ storybook: core_1.Flags.boolean({
58
+ description: 'With Storybook added to the project',
59
+ default: false,
60
+ }),
54
61
  };
@@ -7,33 +7,105 @@ const core_1 = require("@oclif/core");
7
7
  const axios_1 = __importDefault(require("axios"));
8
8
  const base_1 = __importDefault(require("../base"));
9
9
  const access_token_1 = require("../lib/auth/access-token");
10
+ const create_form_data_1 = __importDefault(require("../lib/deploy/create-form-data"));
10
11
  const pack_project_1 = __importDefault(require("../lib/deploy/pack-project"));
11
12
  const upload_assets_1 = __importDefault(require("../lib/deploy/upload-assets"));
12
- const create_form_data_1 = __importDefault(require("../lib/deploy/create-form-data"));
13
13
  class Deploy extends base_1.default {
14
14
  async run() {
15
+ try {
16
+ await this.parse(Deploy);
17
+ }
18
+ catch (err) {
19
+ console.log('err', err);
20
+ }
15
21
  const { flags } = await this.parse(Deploy);
16
22
  const appConfig = await this.getAppConfig(flags.qa ? 'qa' : 'production');
17
23
  const accessToken = (0, access_token_1.getAccessToken)(appConfig.auth.audience);
18
- await (0, pack_project_1.default)();
24
+ const linkTypeServiceUrl = `${flags.endpoint ?? appConfig.link_types_url}/link-types`;
25
+ const isForceUpdate = !!flags['force-update'];
26
+ const packedFiles = await (0, pack_project_1.default)();
27
+ this.log(`\n===`);
28
+ if (isForceUpdate) {
29
+ this.log('⚠️ "--force-update" flag will impact the existing PUBLISHED Link Apps.\n');
30
+ }
31
+ if (packedFiles?.length) {
32
+ this.log('Ready to upload the following files:');
33
+ packedFiles.forEach((file) => {
34
+ this.log(` - ${file}`);
35
+ });
36
+ }
37
+ const userConfirmDeploy = await core_1.CliUx.ux.prompt(`❓ Do you want to proceed? (Y/n)`, { required: false });
38
+ if (userConfirmDeploy?.toLowerCase() !== 'y') {
39
+ this.log('Aborted!');
40
+ return;
41
+ }
19
42
  const form = (0, create_form_data_1.default)(flags.path);
20
- const url = `${flags.endpoint ?? appConfig.link_types_url}/link-types`;
43
+ let linkTypeErrorCode = null;
21
44
  try {
22
- const result = await (0, upload_assets_1.default)(url, form, accessToken, flags.update, flags['force-update']);
23
- this.log(`Draft Link App successfully ${flags.update ? 'updated' : 'uploaded'}\n`);
24
- this.log(JSON.stringify(result, null, 2));
45
+ const result = await (0, upload_assets_1.default)(linkTypeServiceUrl, form, accessToken, flags.update, isForceUpdate);
46
+ await this.processSuccess(flags, result);
25
47
  }
26
48
  catch (err) {
27
- this.log('There was an error while uploading Link App assets\n');
49
+ let throwError = true;
28
50
  if (axios_1.default.isAxiosError(err)) {
29
- if (err.response) {
30
- this.error(JSON.stringify(err.response.data, null, 2));
51
+ if (err.response?.data) {
52
+ linkTypeErrorCode = err.response?.data?.['link_types_error_code'] ?? null;
53
+ if (linkTypeErrorCode === 'LINK_TYPE_ALREADY_EXISTS') {
54
+ throwError = false;
55
+ }
56
+ else {
57
+ this.error(JSON.stringify(err.response.data, null, 2));
58
+ }
31
59
  }
32
- this.error(err.message);
60
+ else {
61
+ this.error(err.message);
62
+ }
63
+ }
64
+ if (throwError) {
65
+ throw err;
66
+ }
67
+ }
68
+ if (linkTypeErrorCode === 'LINK_TYPE_ALREADY_EXISTS' && !flags.update && !isForceUpdate) {
69
+ this.log(`\n⚠️ This Link Type already exists! Do you want to update it?\n`);
70
+ const userConfirmUpdate = await core_1.CliUx.ux.prompt(`❓ Do you want to proceed? (Y/n)`, { required: false });
71
+ if (userConfirmUpdate.toLowerCase() !== 'y') {
72
+ this.log('Aborted!');
73
+ return;
74
+ }
75
+ try {
76
+ const result = await (0, upload_assets_1.default)(linkTypeServiceUrl, form, accessToken, true, isForceUpdate);
77
+ await this.processSuccess(flags, result);
78
+ }
79
+ catch (err) {
80
+ if (axios_1.default.isAxiosError(err)) {
81
+ if (err.response?.data) {
82
+ this.error(JSON.stringify(err.response.data, null, 2));
83
+ }
84
+ else {
85
+ this.error(err.message);
86
+ }
87
+ }
88
+ throw err;
33
89
  }
34
- throw err;
35
90
  }
36
91
  }
92
+ async processSuccess(flags, result) {
93
+ this.log(`\n===`);
94
+ this.log(JSON.stringify(result, null, 2));
95
+ const linkTypeId = result.linkType.linkTypeId;
96
+ const buildId = result.build.id;
97
+ const linkAppBuildUrl = flags.qa
98
+ ? `https://buildkite.com/linktree/link-types-ci-service/builds/${buildId}`
99
+ : `https://linkapp-ci.replit.app/?linkTypeId=${linkTypeId}&buildId=${buildId}`;
100
+ const linkAppCreateUrl = flags.qa
101
+ ? `https://qa.linktr.ee/admin?action=create-link&linkType=${linkTypeId}`
102
+ : `https://linktr.ee/admin?action=create-link&linkType=${linkTypeId}`;
103
+ this.log(`\n===`);
104
+ this.log(`✅ Draft Link App successfully ${flags.update ? 'updated' : 'uploaded'}`);
105
+ this.log(` - Link App ID: ${linkTypeId}`);
106
+ this.log(` - Check Link App build: ${linkAppBuildUrl}`);
107
+ this.log(` - Create Link App: ${linkAppCreateUrl}`);
108
+ }
37
109
  }
38
110
  exports.default = Deploy;
39
111
  Deploy.description = 'Deploy your Link App and test it on Linktr.ee';
@@ -45,16 +117,17 @@ Deploy.flags = {
45
117
  update: core_1.Flags.boolean({
46
118
  description: 'Push update for existing Link App',
47
119
  }),
120
+ // Test local link-types service, with --endpoint http://localhost:8081
48
121
  endpoint: core_1.Flags.string({
49
- description: 'Custom API endpoint to push assets to',
122
+ description: 'Custom API endpoint to push assets to. Admin use only.',
50
123
  hidden: true,
51
124
  }),
52
125
  qa: core_1.Flags.boolean({
53
- description: 'Use QA environment',
126
+ description: 'Use QA environment. Admin use only.',
54
127
  hidden: true,
55
128
  }),
56
129
  'force-update': core_1.Flags.boolean({
57
- description: 'Allow Link Type Admins to push updates to Link Apps in PUBLISHED state',
130
+ description: 'Allow Link Type Admins to push updates to Link Apps in PUBLISHED state. Admin use only.',
58
131
  hidden: true,
59
132
  }),
60
133
  };
@@ -54,11 +54,11 @@ GrantAccess.args = [
54
54
  ];
55
55
  GrantAccess.flags = {
56
56
  endpoint: core_1.Flags.string({
57
- description: 'Custom API endpoint to push assets to',
57
+ description: 'Custom API endpoint to push assets to. Admin use only.',
58
58
  hidden: true,
59
59
  }),
60
60
  qa: core_1.Flags.boolean({
61
- description: 'Use QA environment',
61
+ description: 'Use QA environment. Admin use only.',
62
62
  hidden: true,
63
63
  }),
64
64
  };
@@ -34,7 +34,7 @@ exports.default = Login;
34
34
  Login.description = 'Login using your Linktree credentials to deploy Link Apps';
35
35
  Login.flags = {
36
36
  qa: core_1.Flags.boolean({
37
- description: 'Use QA environment',
37
+ description: 'Use QA environment. Admin use only.',
38
38
  hidden: true,
39
39
  }),
40
40
  };
@@ -24,7 +24,7 @@ exports.default = Logout;
24
24
  Logout.description = 'Logout and clear browser session';
25
25
  Logout.flags = {
26
26
  qa: core_1.Flags.boolean({
27
- description: 'Use QA environment',
27
+ description: 'Use QA environment. Admin use only.',
28
28
  hidden: true,
29
29
  }),
30
30
  };
@@ -8,12 +8,15 @@ 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 (template, targetDir) => {
11
+ const createProject = async (template, targetDir, options) => {
12
12
  const templateDir = path_1.default.resolve(BaseTemplatesDir, template);
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);
16
16
  await fs_extra_1.default.move(`${targetDir}/gitignore`, `${targetDir}/.gitignore`);
17
+ if (options.hasStorybook) {
18
+ await fs_extra_1.default.copy(path_1.default.resolve(BaseTemplatesDir, 'stories'), path_1.default.resolve(targetDir, 'stories'));
19
+ }
17
20
  // remove types that are only required when building the `create-link-app` project
18
21
  if (await fs_extra_1.default.existsSync(`${targetDir}/src/types/hack.d.ts`)) {
19
22
  await fs_extra_1.default.unlink(`${targetDir}/src/types/hack.d.ts`);
@@ -34,11 +34,6 @@ const OptionalAssets = [
34
34
  fileName: 'url_match_rules.json',
35
35
  contentType: 'application/json',
36
36
  },
37
- {
38
- key: 'urlRules',
39
- fileName: 'urlRules.json',
40
- contentType: 'application/json',
41
- },
42
37
  ];
43
38
  /**
44
39
  * Create a readable multipart/form-data stream which includes the Link App asset files.
@@ -1,22 +1,43 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
27
  };
5
28
  Object.defineProperty(exports, "__esModule", { value: true });
6
- const archiver_1 = __importDefault(require("archiver"));
29
+ const archiver_1 = __importStar(require("archiver"));
7
30
  const fs_1 = __importDefault(require("fs"));
8
31
  const path_1 = __importDefault(require("path"));
9
32
  /**
10
- * Pack the Link App source files into `package.tgz`. This includes the following:
11
- * - `src/`
12
- * - `schema.json`
13
- * - `package.json`
14
- * - `package-lock.json` - *optional*
15
- * - `yarn.lock` - *optional*
16
- * - `tsconfig.json` - *optional*
33
+ * Pack the Link App source files into `package.tgz`. This includes the above required/optional directories and files.
17
34
  */
18
35
  async function packProject() {
19
- return new Promise((resolve, reject) => {
36
+ const requiredDirs = ['src'];
37
+ const requiredFiles = ['package.json', 'manifest.json'];
38
+ const optionalFiles = ['package-lock.json', 'yarn.lock', 'tsconfig.json', 'url-match-rules.json', 'settings.json', 'icon.svg'];
39
+ const packedFiles = [];
40
+ await new Promise((resolve, reject) => {
20
41
  const outputPath = resolveProjPath('package.tgz');
21
42
  const outputWriteStream = fs_1.default.createWriteStream(outputPath);
22
43
  const handleSuccess = () => resolve();
@@ -31,30 +52,36 @@ async function packProject() {
31
52
  gzipOptions: { level: 9 },
32
53
  });
33
54
  archive.on('warning', (err) => {
34
- if (err?.code === 'ENOENT') {
35
- // package, lock files, tsconfig, and schema files may or may not exist
36
- const fileName = path_1.default.basename(err.path);
37
- if (fileName === 'package.json' ||
38
- fileName === 'package-lock.json' ||
39
- fileName === 'yarn.lock' ||
40
- fileName === 'tsconfig.json' ||
41
- fileName.endsWith('.jtd.json')) {
42
- return;
43
- }
44
- }
45
55
  handleFailure(err);
46
56
  });
47
57
  archive.on('error', (err) => {
48
58
  handleFailure(err);
49
59
  });
50
60
  archive.pipe(outputWriteStream);
51
- archive.file(resolveProjPath('package.json'), { name: 'package.json' });
52
- archive.file(resolveProjPath('package-lock.json'), { name: 'package-lock.json' });
53
- archive.file(resolveProjPath('yarn.lock'), { name: 'yarn.lock' });
54
- archive.file(resolveProjPath('tsconfig.json'), { name: 'tsconfig.json' });
55
- archive.directory(resolveProjPath('src/'), 'src');
61
+ requiredFiles.forEach((fileName) => {
62
+ if (!fs_1.default.existsSync(resolveProjPath(fileName))) {
63
+ handleFailure(new archiver_1.ArchiverError(`File ${fileName} does not exist`, 'ENOENT'));
64
+ }
65
+ archive.file(resolveProjPath(fileName), { name: fileName });
66
+ packedFiles.push(fileName);
67
+ });
68
+ optionalFiles.forEach((fileName) => {
69
+ if (fs_1.default.existsSync(resolveProjPath(fileName))) {
70
+ archive.file(resolveProjPath(fileName), { name: fileName });
71
+ packedFiles.push(fileName);
72
+ }
73
+ });
74
+ requiredDirs.forEach((dirName) => {
75
+ if (!fs_1.default.existsSync(resolveProjPath(dirName)) || !fs_1.default.statSync(resolveProjPath(dirName)).isDirectory()) {
76
+ handleFailure(new archiver_1.ArchiverError(`Directory ${dirName} does not exist`, 'ENOENT'));
77
+ }
78
+ archive.directory(resolveProjPath(`${dirName}/`), dirName);
79
+ packedFiles.push(`${dirName}/`);
80
+ });
56
81
  archive.finalize();
57
82
  });
83
+ packedFiles.sort();
84
+ return packedFiles;
58
85
  }
59
86
  const resolveProjPath = (relativePath) => path_1.default.resolve(process.cwd(), relativePath);
60
87
  exports.default = packProject;
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.4.0",
2
+ "version": "1.5.0",
3
3
  "commands": {
4
4
  "build": {
5
5
  "id": "build",
@@ -60,6 +60,12 @@
60
60
  "char": "p",
61
61
  "description": "Path to create the project in",
62
62
  "multiple": false
63
+ },
64
+ "storybook": {
65
+ "name": "storybook",
66
+ "type": "boolean",
67
+ "description": "With Storybook added to the project",
68
+ "allowNo": false
63
69
  }
64
70
  },
65
71
  "args": {
@@ -96,21 +102,21 @@
96
102
  "endpoint": {
97
103
  "name": "endpoint",
98
104
  "type": "option",
99
- "description": "Custom API endpoint to push assets to",
105
+ "description": "Custom API endpoint to push assets to. Admin use only.",
100
106
  "hidden": true,
101
107
  "multiple": false
102
108
  },
103
109
  "qa": {
104
110
  "name": "qa",
105
111
  "type": "boolean",
106
- "description": "Use QA environment",
112
+ "description": "Use QA environment. Admin use only.",
107
113
  "hidden": true,
108
114
  "allowNo": false
109
115
  },
110
116
  "force-update": {
111
117
  "name": "force-update",
112
118
  "type": "boolean",
113
- "description": "Allow Link Type Admins to push updates to Link Apps in PUBLISHED state",
119
+ "description": "Allow Link Type Admins to push updates to Link Apps in PUBLISHED state. Admin use only.",
114
120
  "hidden": true,
115
121
  "allowNo": false
116
122
  }
@@ -191,14 +197,14 @@
191
197
  "endpoint": {
192
198
  "name": "endpoint",
193
199
  "type": "option",
194
- "description": "Custom API endpoint to push assets to",
200
+ "description": "Custom API endpoint to push assets to. Admin use only.",
195
201
  "hidden": true,
196
202
  "multiple": false
197
203
  },
198
204
  "qa": {
199
205
  "name": "qa",
200
206
  "type": "boolean",
201
- "description": "Use QA environment",
207
+ "description": "Use QA environment. Admin use only.",
202
208
  "hidden": true,
203
209
  "allowNo": false
204
210
  }
@@ -229,7 +235,7 @@
229
235
  "qa": {
230
236
  "name": "qa",
231
237
  "type": "boolean",
232
- "description": "Use QA environment",
238
+ "description": "Use QA environment. Admin use only.",
233
239
  "hidden": true,
234
240
  "allowNo": false
235
241
  }
@@ -249,7 +255,7 @@
249
255
  "qa": {
250
256
  "name": "qa",
251
257
  "type": "boolean",
252
- "description": "Use QA environment",
258
+ "description": "Use QA environment. Admin use only.",
253
259
  "hidden": true,
254
260
  "allowNo": false
255
261
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linktr.ee/create-link-app",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "description": "Create a Link App on Linktr.ee.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "Linktree",
@@ -1,18 +1,18 @@
1
- import React from 'react'
2
- import { ComponentStory, ComponentMeta } from '@storybook/react'
3
- import manifest from '../../manifest.json'
4
- import fixture from '../../fixtures/props-data.json'
1
+ import { ComponentMeta, ComponentStory } from "@storybook/react";
2
+ import React from "react";
3
+ import fixture from "../fixtures/props-data.json";
4
+ import manifest from "../manifest.json";
5
5
 
6
- import LinkApp from '../App'
6
+ import LinkApp from "../src/App";
7
7
 
8
- import './tailwind.sb.css'
8
+ import "./tailwind.sb.css";
9
9
 
10
10
  export default {
11
11
  title: `Link App/${manifest.name}`,
12
12
  component: LinkApp,
13
- tags: ['autodocs'],
13
+ tags: ["autodocs"],
14
14
  parameters: {
15
- layout: 'fullscreen',
15
+ layout: "fullscreen",
16
16
  },
17
17
  args: {
18
18
  ...fixture,
@@ -25,68 +25,68 @@ export default {
25
25
  },
26
26
  },
27
27
  },
28
- } as ComponentMeta<typeof LinkApp>
28
+ } as ComponentMeta<typeof LinkApp>;
29
29
 
30
30
  export const Stack: ComponentStory<typeof LinkApp> = (args) => (
31
31
  <div
32
- data-link-type-id={__LINK_TYPE_SLUG__}
32
+ data-link-type-id={manifest.name}
33
33
  style={{
34
- margin: '0.5rem',
34
+ margin: "0.5rem",
35
35
  width: 374,
36
36
  height: 80,
37
37
  }}
38
38
  >
39
39
  <LinkApp {...args} />
40
40
  </div>
41
- )
41
+ );
42
42
  Stack.args = {
43
- __layout: 'STACK',
44
- }
43
+ __layout: "STACK",
44
+ };
45
45
 
46
46
  export const Grid: ComponentStory<typeof LinkApp> = (args) => (
47
47
  <div
48
- data-link-type-id={__LINK_TYPE_SLUG__}
48
+ data-link-type-id={manifest.name}
49
49
  style={{
50
- margin: '0.5rem',
50
+ margin: "0.5rem",
51
51
  width: 183,
52
52
  height: 240,
53
53
  }}
54
54
  >
55
55
  <LinkApp {...args} />
56
56
  </div>
57
- )
57
+ );
58
58
  Grid.args = {
59
- __layout: 'GRID',
60
- }
59
+ __layout: "GRID",
60
+ };
61
61
 
62
62
  export const Carousel: ComponentStory<typeof LinkApp> = (args) => (
63
63
  <div
64
- data-link-type-id={__LINK_TYPE_SLUG__}
64
+ data-link-type-id={manifest.name}
65
65
  style={{
66
- margin: '0.5rem',
66
+ margin: "0.5rem",
67
67
  width: 224,
68
68
  height: 296,
69
69
  }}
70
70
  >
71
71
  <LinkApp {...args} />
72
72
  </div>
73
- )
73
+ );
74
74
  Carousel.args = {
75
- __layout: 'CAROUSEL',
76
- }
75
+ __layout: "CAROUSEL",
76
+ };
77
77
 
78
78
  export const Featured: ComponentStory<typeof LinkApp> = (args) => (
79
79
  <div
80
- data-link-type-id={__LINK_TYPE_SLUG__}
80
+ data-link-type-id={manifest.name}
81
81
  style={{
82
- margin: '0.5rem',
82
+ margin: "0.5rem",
83
83
  width: 374,
84
84
  height: 374,
85
85
  }}
86
86
  >
87
87
  <LinkApp {...args} />
88
88
  </div>
89
- )
89
+ );
90
90
  Featured.args = {
91
- __layout: 'FEATURED',
92
- }
91
+ __layout: "FEATURED",
92
+ };
File without changes