@linktr.ee/create-link-app 2.0.0 → 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.
@@ -1,5 +1,4 @@
1
1
  "use strict";
2
- /* eslint-disable @typescript-eslint/no-non-null-assertion */
3
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
4
  };
@@ -46,17 +45,46 @@ async function default_1(env, options) {
46
45
  const linkTypeSlug = (0, slugify_1.default)(linkTypeName, { lower: true });
47
46
  const linkTypeId = (0, camelcase_1.default)(linkTypeSlug, { pascalCase: true });
48
47
  const hasTsconfig = fileExists(path_1.default.resolve(appDir, 'tsconfig.json'));
48
+ // Check if we should enable profiling and analysis
49
+ const enableProfiling = options?.profile || false;
49
50
  const config = {
50
51
  target: 'web',
51
52
  mode: env,
52
53
  entry: entryFile,
54
+ // Enable webpack persistent caching for faster subsequent builds
55
+ cache: env === 'production'
56
+ ? {
57
+ type: 'filesystem',
58
+ buildDependencies: {
59
+ config: [__filename],
60
+ },
61
+ }
62
+ : undefined,
53
63
  output: {
54
64
  publicPath: 'auto',
55
65
  path: path_1.default.resolve(appDir, 'dist'),
56
66
  assetModuleFilename: 'images/[hash].[contenthash][ext][query]',
57
67
  chunkFilename: '[id].[contenthash].js',
58
68
  filename: '[name].[contenthash].js',
69
+ // Clean dist folder before each build
70
+ clean: true,
59
71
  },
72
+ // Enable profiling for production builds when requested
73
+ ...(enableProfiling && {
74
+ profile: true,
75
+ stats: {
76
+ timings: true,
77
+ modules: true,
78
+ moduleTrace: true,
79
+ reasons: true,
80
+ usedExports: true,
81
+ providedExports: true,
82
+ optimizationBailout: true,
83
+ errorDetails: true,
84
+ logging: 'verbose',
85
+ loggingTrace: true,
86
+ },
87
+ }),
60
88
  module: {
61
89
  rules: [
62
90
  {
@@ -70,11 +98,19 @@ async function default_1(env, options) {
70
98
  [require.resolve('@babel/preset-react'), { runtime: 'automatic' }],
71
99
  ],
72
100
  plugins: [require.resolve('@babel/plugin-transform-runtime')],
101
+ // Enable caching for faster rebuilds
102
+ cacheDirectory: true,
103
+ cacheCompression: false, // Don't compress cache files for faster access
73
104
  },
74
105
  },
75
106
  {
76
107
  test: /\.(png|jpe?g|gif|svg)$/i,
77
- type: 'asset/inline', // <-- this builds all images into data URLs, is that what you want?
108
+ type: 'asset',
109
+ parser: {
110
+ dataUrlCondition: {
111
+ maxSize: 8 * 1024, // Only inline images smaller than 8KB
112
+ },
113
+ },
78
114
  },
79
115
  {
80
116
  test: /\.css$/,
@@ -112,18 +148,32 @@ async function default_1(env, options) {
112
148
  template: htmlTemplateFile,
113
149
  excludeChunks: [`LinkApp_${linkTypeId}`],
114
150
  }),
151
+ // Add progress plugin for better build feedback
152
+ new webpack_1.ProgressPlugin({
153
+ activeModules: true,
154
+ entries: true,
155
+ modules: true,
156
+ modulesCount: 5000,
157
+ profile: enableProfiling,
158
+ dependencies: true,
159
+ dependenciesCount: 10000,
160
+ percentBy: 'entries',
161
+ }),
115
162
  ],
116
163
  };
117
164
  // Type checking on a separate process - @babel/preset-typescript only handles transpilation
118
165
  if (hasTsconfig) {
166
+ config.plugins = config.plugins || [];
119
167
  config.plugins.push(new fork_ts_checker_webpack_plugin_1.default());
120
168
  }
121
169
  if (env === 'development') {
122
170
  config.devtool = 'cheap-module-source-map';
171
+ config.resolve = config.resolve || {};
123
172
  config.resolve.alias = {
124
173
  ...config.resolve.alias,
125
174
  '@linktr.ee/extension-dev-data': path_1.default.resolve(appDir, 'fixtures', 'props-data.json'),
126
175
  };
176
+ config.plugins = config.plugins || [];
127
177
  config.plugins.push(new mini_css_extract_plugin_1.default({
128
178
  filename: '[name].[contenthash].css',
129
179
  chunkFilename: '[id].[contenthash].css',
@@ -142,6 +192,91 @@ async function default_1(env, options) {
142
192
  if (useSheetEntry) {
143
193
  exposedEntryPoints['./Sheet'] = path_1.default.resolve(appDir, 'src/Sheet');
144
194
  }
195
+ // Add bundle analyzer if requested
196
+ if (enableProfiling) {
197
+ try {
198
+ // Dynamically import webpack-bundle-analyzer to avoid requiring it as a hard dependency
199
+ // eslint-disable-next-line @typescript-eslint/no-var-requires, @typescript-eslint/no-explicit-any
200
+ const bundleAnalyzer = require('webpack-bundle-analyzer');
201
+ const BundleAnalyzerPlugin = bundleAnalyzer.BundleAnalyzerPlugin;
202
+ config.plugins?.push(new BundleAnalyzerPlugin({
203
+ analyzerMode: 'static',
204
+ reportFilename: path_1.default.resolve(appDir, 'dist', 'bundle-report.html'),
205
+ openAnalyzer: false,
206
+ generateStatsFile: true,
207
+ statsFilename: path_1.default.resolve(appDir, 'dist', 'webpack-stats.json'),
208
+ }));
209
+ }
210
+ catch (error) {
211
+ console.warn('⚠️ webpack-bundle-analyzer not installed. Run: npm install --save-dev webpack-bundle-analyzer');
212
+ console.warn(' Bundle analysis will be skipped.');
213
+ }
214
+ }
215
+ // Optimize build performance and bundle splitting
216
+ config.optimization = {
217
+ minimize: true,
218
+ minimizer: [
219
+ // Configure TerserPlugin for better performance
220
+ '...',
221
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
222
+ new (require('terser-webpack-plugin'))({
223
+ parallel: true,
224
+ terserOptions: {
225
+ compress: {
226
+ // Reduce compression passes for faster builds
227
+ passes: 1,
228
+ drop_console: true,
229
+ drop_debugger: true,
230
+ },
231
+ mangle: {
232
+ safari10: true, // Fix Safari 10+ bug
233
+ },
234
+ format: {
235
+ comments: false, // Remove all comments
236
+ },
237
+ },
238
+ extractComments: false, // Don't create separate LICENSE files
239
+ }),
240
+ ],
241
+ splitChunks: {
242
+ chunks: 'all',
243
+ cacheGroups: {
244
+ // Separate vendor libraries for better caching
245
+ vendor: {
246
+ test: /[\\/]node_modules[\\/]/,
247
+ name: 'vendors',
248
+ chunks: 'all',
249
+ priority: 10,
250
+ },
251
+ // Separate large UI library
252
+ uiKit: {
253
+ test: /[\\/]node_modules[\\/]@linktr\.ee[\\/]ui-link-kit[\\/]/,
254
+ name: 'ui-kit',
255
+ chunks: 'all',
256
+ priority: 20,
257
+ },
258
+ // Separate icon libraries
259
+ icons: {
260
+ test: /[\\/]node_modules[\\/].*icons.*[\\/]/,
261
+ name: 'icons',
262
+ chunks: 'all',
263
+ priority: 15,
264
+ },
265
+ // Common code shared between chunks
266
+ common: {
267
+ name: 'common',
268
+ minChunks: 2,
269
+ chunks: 'all',
270
+ priority: 5,
271
+ reuseExistingChunk: true,
272
+ },
273
+ },
274
+ },
275
+ // Enable runtime chunk for better caching
276
+ runtimeChunk: {
277
+ name: 'runtime',
278
+ },
279
+ };
145
280
  // TODO: Figure out how to manage externals with Module Federation
146
281
  // Possibly make the index.html a host app, via separate webpack config
147
282
  // config.externals = {
@@ -154,7 +289,7 @@ async function default_1(env, options) {
154
289
  maxAssetSize: 350000,
155
290
  hints: 'warning',
156
291
  };
157
- config.plugins.push(new webpack_1.DefinePlugin({
292
+ config.plugins?.push(new webpack_1.DefinePlugin({
158
293
  __ALLOW_ANY_ORIGIN__: options?.allowAnyOrigin,
159
294
  __LINK_TYPE_SLUG__: JSON.stringify(linkTypeSlug),
160
295
  __LINK_TYPE_ID__: JSON.stringify(linkTypeId),
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.0.0",
2
+ "version": "2.1.0",
3
3
  "commands": {
4
4
  "build": {
5
5
  "id": "build",
@@ -23,6 +23,12 @@
23
23
  "type": "boolean",
24
24
  "description": "Build native components of a Link App ready for publishing to npm",
25
25
  "allowNo": false
26
+ },
27
+ "profile": {
28
+ "name": "profile",
29
+ "type": "boolean",
30
+ "description": "Enable detailed webpack profiling and bundle analysis",
31
+ "allowNo": false
26
32
  }
27
33
  },
28
34
  "args": {}
@@ -38,22 +44,10 @@
38
44
  "hiddenAliases": [],
39
45
  "examples": [
40
46
  "$ create-link-app create my-link-app",
41
- "$ create-link-app create my-link-app --template react",
42
- "$ create-link-app create my-link-app --path my/custom/path"
47
+ "$ create-link-app create my-link-app --path my/custom/path",
48
+ "$ create-link-app create my-link-app --storybook"
43
49
  ],
44
50
  "flags": {
45
- "template": {
46
- "name": "template",
47
- "type": "option",
48
- "char": "t",
49
- "description": "Template to use for the project",
50
- "multiple": false,
51
- "options": [
52
- "react",
53
- "react-ts"
54
- ],
55
- "default": "react-ts"
56
- },
57
51
  "path": {
58
52
  "name": "path",
59
53
  "type": "option",
@@ -176,18 +170,6 @@
176
170
  },
177
171
  "args": {}
178
172
  },
179
- "generate-types": {
180
- "id": "generate-types",
181
- "description": "Generate Typescript types from the schema definitions",
182
- "strict": true,
183
- "pluginName": "@linktr.ee/create-link-app",
184
- "pluginAlias": "@linktr.ee/create-link-app",
185
- "pluginType": "core",
186
- "aliases": [],
187
- "hiddenAliases": [],
188
- "flags": {},
189
- "args": {}
190
- },
191
173
  "grant-access": {
192
174
  "id": "grant-access",
193
175
  "description": "Grant access to other developers to push updates for your Link App",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@linktr.ee/create-link-app",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Create a Link App on Linktr.ee.",
5
5
  "license": "UNLICENSED",
6
6
  "author": "Linktree",
@@ -76,6 +76,7 @@
76
76
  "style-loader": "^3.3.2",
77
77
  "styled-components": "^5.3.5",
78
78
  "tailwindcss": "^3.3.2",
79
+ "terser-webpack-plugin": "^5.3.10",
79
80
  "ts-loader": "^9.4.4",
80
81
  "typescript": "^4.7.3",
81
82
  "webpack": "^5.75.0",
@@ -0,0 +1,10 @@
1
+ {
2
+ "hostnames": [
3
+ "linktr.ee"
4
+ ],
5
+ "patterns": [
6
+ {
7
+ "pathname": "/*"
8
+ }
9
+ ]
10
+ }
@@ -4,7 +4,7 @@
4
4
  "build": "create-link-app build",
5
5
  "login-app": "create-link-app login",
6
6
  "logout-app": "create-link-app logout",
7
- "upload": "create-link-app deploy",
7
+ "deploy": "create-link-app deploy",
8
8
  "dev": "create-link-app dev",
9
9
  "prepack": "create-link-app build --native",
10
10
  "test-url-match-rules": "create-link-app test-url-match-rules"
@@ -2,5 +2,5 @@ module.exports = {
2
2
  plugins: {
3
3
  tailwindcss: {},
4
4
  autoprefixer: {},
5
- }
6
- }
5
+ },
6
+ }
@@ -1,8 +1,8 @@
1
1
  /** @type {import('tailwindcss').Config} */
2
2
  module.exports = {
3
- content: ["./src/**/*.{js,jsx,ts,tsx}"],
3
+ content: ['./src/**/*.{js,jsx,ts,tsx}'],
4
4
  theme: {
5
5
  extend: {},
6
6
  },
7
7
  plugins: [],
8
- };
8
+ }
@@ -1,102 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- const path_1 = __importDefault(require("path"));
7
- const base_1 = __importDefault(require("../base"));
8
- const child_process_1 = require("child_process");
9
- class MissingDependencyException extends Error {
10
- constructor(packageName) {
11
- super(`'${packageName}' is required on your system. Please refer to the create-link-app README`);
12
- }
13
- }
14
- const ucfirst = (str) => {
15
- return str.charAt(0).toUpperCase() + str.slice(1);
16
- };
17
- class GenerateTypes extends base_1.default {
18
- constructor() {
19
- super(...arguments);
20
- this.schemaDirPath = path_1.default.join(process.cwd(), '/schema');
21
- this.tmpPath = path_1.default.join(process.cwd(), '/__temp__');
22
- this.typesPath = path_1.default.join(process.cwd(), '/src/types/schema');
23
- }
24
- async run() {
25
- try {
26
- // Check for necessary deps
27
- await this.dependOn('npx');
28
- await this.dependOn('jtd-codegen');
29
- await this.log('✓ All dependencies present');
30
- // Ensure necessary directories exist
31
- await this.cmd(`mkdir -p ${this.tmpPath} && touch ${this.tmpPath}/index.ts`);
32
- await this.cmd(`mkdir -p ${this.typesPath} && touch ${this.typesPath}/index.ts`);
33
- await this.log('✓ Files prepped for generation');
34
- // Wipe existing types & drop comments
35
- await this.cmd(`> ${this.typesPath}/index.ts`);
36
- await this.cmd(`echo "/**" >> ${this.typesPath}/index.ts`);
37
- await this.cmd(`echo " * These types were autogenerated by @linktr.ee/create-link-app" >> ${this.typesPath}/index.ts`);
38
- await this.cmd(`echo " * https://www.npmjs.com/package/@linktr.ee/create-link-app " >> ${this.typesPath}/index.ts`);
39
- await this.cmd(`echo " */" >> ${this.typesPath}/index.ts`);
40
- // Clean up file formatting
41
- await this.cmd(`npx prettier ${this.typesPath}/index.ts --write`);
42
- await this.log('✓ All types created successfully');
43
- // Garbage collect temporary dir
44
- await this.cmd(`rm -rf ${this.tmpPath}`);
45
- await this.log('✓ Cleanup complete');
46
- }
47
- catch (e) {
48
- await this.log(`𐄂 ERROR: ${e}`);
49
- }
50
- }
51
- /**
52
- * Generates a Typescript type for the schema defined in schema/<schemaName>.jtd.json
53
- * and appends that type to src/types/schema/index.ts
54
- */
55
- async generateAndAppendSchemaType(schemaName) {
56
- // Generate schema
57
- await this.cmd(`jtd-codegen ${this.schemaDirPath}/${schemaName}.jtd.json --typescript-out ${this.tmpPath} --root-name=${ucfirst(schemaName)}Data`);
58
- // Strip jtd comment
59
- await this.cmd(`sed '/^\\/\\//d' ${this.tmpPath}/index.ts > ${this.tmpPath}/parsed.ts`);
60
- // Append to final file
61
- await this.cmd(`cat ${this.tmpPath}/parsed.ts >> ${this.typesPath}/index.ts`);
62
- }
63
- async dependOn(dependencyName) {
64
- try {
65
- await this.cmd(`command -v ${dependencyName} || exit 1`);
66
- }
67
- catch (e) {
68
- throw new MissingDependencyException('prettier');
69
- }
70
- }
71
- /**
72
- * Run a command with spawn effectively synchronously
73
- */
74
- async cmd(command, opts = {
75
- shell: true,
76
- stdio: 'ignore',
77
- }, onError) {
78
- await new Promise((resolve, reject) => {
79
- (0, child_process_1.spawn)(command, opts)
80
- .on('error', (e) => {
81
- reject(e);
82
- })
83
- .on('exit', (code) => {
84
- if (code === 1)
85
- reject({ exitCode: code });
86
- })
87
- .on('close', () => {
88
- resolve();
89
- });
90
- }).catch((e) => {
91
- if (onError) {
92
- onError();
93
- }
94
- else {
95
- throw e;
96
- }
97
- });
98
- return;
99
- }
100
- }
101
- exports.default = GenerateTypes;
102
- GenerateTypes.description = 'Generate Typescript types from the schema definitions';