@platformos/platformos-common 0.0.9 → 0.0.11

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/src/path-utils.ts CHANGED
@@ -8,28 +8,65 @@
8
8
  * - getFileType() classifies any URI to a PlatformOSFileType
9
9
  * - isPage(), isLayout(), isPartial() etc. are convenience wrappers around getFileType()
10
10
  *
11
- * Pattern precision: each type uses exact path segment patterns (/app/{dir}/ or
12
- * /(public|private)/{dir}/) so that nested paths don't produce false positives.
13
- * For example, app/lib/smses/file.liquid is correctly identified as Partial
14
- * (matches /app/lib/), not Sms (does not match /app/smses/).
11
+ * Source of truth: app/services/app_builder/services/converters_config.rb and
12
+ * app/models/concerns/deployable.rb in the platformOS server codebase.
13
+ *
14
+ * DIR_PREFIX (Ruby): ^/?((marketplace_builder|app)/|modules/(.+)(private|public)/)?
15
+ * This means files can live under:
16
+ * - app/{dir}/
17
+ * - marketplace_builder/{dir}/ (legacy alias for app/)
18
+ * - modules/{name}/(public|private)/{dir}/
19
+ * - app/modules/{name}/(public|private)/{dir}/
15
20
  */
16
21
 
17
22
  import { UriString } from './AbstractFileSystem';
18
23
 
19
24
  /**
20
- * File types that exist in a platformOS app, each mapping to one or more
21
- * canonical directory names relative to the app root or module access level.
25
+ * File types that exist in a platformOS app, each corresponding to a server-side
26
+ * converter that processes the file on deploy.
27
+ *
28
+ * Liquid types: Page, Layout, Partial, Authorization, Email, ApiCall, Sms, Migration, FormConfiguration
29
+ * YAML types: CustomModelType, InstanceProfileType, TransactableType, Translation
30
+ * GraphQL types: GraphQL
31
+ * Binary/other: Asset
22
32
  */
23
33
  export enum PlatformOSFileType {
34
+ // ── Liquid ──────────────────────────────────────────────────────────────────
35
+ /** views/pages/ or pages/ → PageConverter */
24
36
  Page = 'Page',
37
+ /** views/layouts/ → LiquidViewConverter (layouts) */
25
38
  Layout = 'Layout',
39
+ /** views/partials/ or lib/ → LiquidViewConverter (partials) */
26
40
  Partial = 'Partial',
41
+ /** authorization_policies/ → AuthorizationPolicyConverter */
27
42
  Authorization = 'Authorization',
43
+ /** emails/ or notifications/email_notifications/ → EmailNotificationConverter */
28
44
  Email = 'Email',
45
+ /** api_calls/ or notifications/api_call_notifications/ → ApiCallNotificationConverter */
29
46
  ApiCall = 'ApiCall',
47
+ /** smses/ or notifications/sms_notifications/ → SmsNotificationConverter */
30
48
  Sms = 'Sms',
49
+ /** migrations/ → MigrationConverter */
31
50
  Migration = 'Migration',
51
+ /** form_configurations/ or forms/ → FormConfigurationConverter */
52
+ FormConfiguration = 'FormConfiguration',
53
+
54
+ // ── YAML ────────────────────────────────────────────────────────────────────
55
+ /** custom_model_types/, model_schemas/, or schema/ → CustomModelTypeConverter */
56
+ CustomModelType = 'CustomModelType',
57
+ /** instance_profile_types/, user_profile_types/, or user_profile_schemas/ → InstanceProfileTypeConverter */
58
+ InstanceProfileType = 'InstanceProfileType',
59
+ /** transactable_types/ → TransactableTypeConverter */
60
+ TransactableType = 'TransactableType',
61
+ /** translations/ → TranslationConverter */
62
+ Translation = 'Translation',
63
+
64
+ // ── GraphQL ─────────────────────────────────────────────────────────────────
65
+ /** graphql/ or graph_queries/ → GraphQueryConverter */
32
66
  GraphQL = 'GraphQL',
67
+
68
+ // ── Binary/other ────────────────────────────────────────────────────────────
69
+ /** assets/ → AssetConverter */
33
70
  Asset = 'Asset',
34
71
  }
35
72
 
@@ -37,30 +74,48 @@ export enum PlatformOSFileType {
37
74
  * The single source of truth for the platformOS directory structure.
38
75
  *
39
76
  * Maps each file type to its canonical directory name(s) relative to:
40
- * - the app root: app/{dir}/
77
+ * - the app root: (app|marketplace_builder)/{dir}/
41
78
  * - a module access level: modules/{name}/(public|private)/{dir}/
42
79
  * - a nested module access level: app/modules/{name}/(public|private)/{dir}/
43
80
  *
81
+ * Multiple dirs per type represent canonical + legacy aliases from the server
82
+ * converters_config.rb FULL_PHYSICAL_PATH regexes.
83
+ *
44
84
  * Types with multiple dirs (e.g. Partial) will match any of their dirs.
45
- * The first matching type wins, so order of evaluation matters for overlapping
46
- * paths — but exact segment matching means dirs don't overlap in practice.
85
+ * Order within each array doesn't matter for matching. Across types,
86
+ * exact segment matching prevents false positives between overlapping paths
87
+ * (e.g. app/lib/smses/ → Partial, not Sms).
47
88
  */
48
89
  export const FILE_TYPE_DIRS: Readonly<Record<PlatformOSFileType, readonly string[]>> = {
49
- [PlatformOSFileType.Page]: ['views/pages'],
90
+ // Liquid
91
+ [PlatformOSFileType.Page]: ['views/pages', 'pages'],
50
92
  [PlatformOSFileType.Layout]: ['views/layouts'],
51
93
  [PlatformOSFileType.Partial]: ['views/partials', 'lib'],
52
94
  [PlatformOSFileType.Authorization]: ['authorization_policies'],
53
- [PlatformOSFileType.Email]: ['emails'],
54
- [PlatformOSFileType.ApiCall]: ['api_calls'],
55
- [PlatformOSFileType.Sms]: ['smses'],
95
+ [PlatformOSFileType.Email]: ['emails', 'notifications/email_notifications'],
96
+ [PlatformOSFileType.ApiCall]: ['api_calls', 'notifications/api_call_notifications'],
97
+ [PlatformOSFileType.Sms]: ['smses', 'notifications/sms_notifications'],
56
98
  [PlatformOSFileType.Migration]: ['migrations'],
57
- [PlatformOSFileType.GraphQL]: ['graphql'],
99
+ [PlatformOSFileType.FormConfiguration]: ['form_configurations', 'forms'],
100
+ // YAML
101
+ [PlatformOSFileType.CustomModelType]: ['custom_model_types', 'model_schemas', 'schema'],
102
+ [PlatformOSFileType.InstanceProfileType]: [
103
+ 'instance_profile_types',
104
+ 'user_profile_types',
105
+ 'user_profile_schemas',
106
+ ],
107
+ [PlatformOSFileType.TransactableType]: ['transactable_types'],
108
+ [PlatformOSFileType.Translation]: ['translations'],
109
+ // GraphQL
110
+ [PlatformOSFileType.GraphQL]: ['graphql', 'graph_queries'],
111
+ // Asset
58
112
  [PlatformOSFileType.Asset]: ['assets'],
59
113
  };
60
114
 
61
115
  /**
62
- * Liquid-containing file types. GraphQL and Asset are excluded because they
63
- * don't contain Liquid code and should not be passed to the Liquid linter.
116
+ * Liquid-containing file types. GraphQL, Asset, and YAML types are excluded
117
+ * because they don't contain Liquid code and should not be passed to the
118
+ * Liquid linter.
64
119
  */
65
120
  const LIQUID_FILE_TYPES = new Set<PlatformOSFileType>([
66
121
  PlatformOSFileType.Page,
@@ -71,16 +126,17 @@ const LIQUID_FILE_TYPES = new Set<PlatformOSFileType>([
71
126
  PlatformOSFileType.ApiCall,
72
127
  PlatformOSFileType.Sms,
73
128
  PlatformOSFileType.Migration,
129
+ PlatformOSFileType.FormConfiguration,
74
130
  ]);
75
131
 
76
132
  /**
77
133
  * Pre-compiled regex per file type, derived entirely from FILE_TYPE_DIRS.
78
134
  *
79
- * For each canonical dir, two pattern alternatives are generated:
80
- * /app/{dir}/ — direct app-level path (e.g. /app/lib/)
81
- * /(public|private)/{dir}/ — module path, covers both:
82
- * modules/{name}/(public|private)/{dir}/
83
- * app/modules/{name}/(public|private)/{dir}/
135
+ * For each canonical dir, three pattern alternatives are generated:
136
+ * /(app|marketplace_builder)/{dir}/ — direct app-level path (modern + legacy root)
137
+ * /(public|private)/{dir}/ — module path, covers both:
138
+ * modules/{name}/(public|private)/{dir}/
139
+ * app/modules/{name}/(public|private)/{dir}/
84
140
  *
85
141
  * Exact path segment matching prevents false positives:
86
142
  * /app/lib/smses/file.liquid → matches /app/lib/ → Partial (NOT Sms)
@@ -89,7 +145,10 @@ const LIQUID_FILE_TYPES = new Set<PlatformOSFileType>([
89
145
  const TYPE_MATCHERS = new Map<PlatformOSFileType, RegExp>(
90
146
  (Object.entries(FILE_TYPE_DIRS) as [PlatformOSFileType, readonly string[]][]).map(
91
147
  ([type, dirs]) => {
92
- const alternatives = dirs.flatMap((dir) => [`/app/${dir}/`, `/(public|private)/${dir}/`]);
148
+ const alternatives = dirs.flatMap((dir) => [
149
+ `/(app|marketplace_builder)/${dir}/`,
150
+ `/(public|private)/${dir}/`,
151
+ ]);
93
152
  return [type, new RegExp(alternatives.join('|'))];
94
153
  },
95
154
  ),
@@ -99,9 +158,13 @@ const TYPE_MATCHERS = new Map<PlatformOSFileType, RegExp>(
99
158
  * Returns the PlatformOSFileType for the given URI, or undefined if the URI
100
159
  * does not belong to any recognized platformOS directory.
101
160
  *
161
+ * Supports both modern (app/) and legacy (marketplace_builder/) app roots,
162
+ * as well as module paths (modules/{name}/public|private/).
163
+ *
102
164
  * @example
103
- * getFileType('file:///root/app/lib/smses/notify.liquid') // → PlatformOSFileType.Partial
104
- * getFileType('file:///root/app/smses/notify.liquid') // → PlatformOSFileType.Sms
165
+ * getFileType('file:///root/app/lib/smses/notify.liquid') // → Partial
166
+ * getFileType('file:///root/app/smses/notify.liquid') // → Sms
167
+ * getFileType('file:///root/marketplace_builder/views/pages/home.liquid') // → Page
105
168
  * getFileType('file:///root/modules/core/generators/templates/lib/create.liquid') // → undefined
106
169
  */
107
170
  export function getFileType(uri: UriString): PlatformOSFileType | undefined {
@@ -113,10 +176,11 @@ export function getFileType(uri: UriString): PlatformOSFileType | undefined {
113
176
 
114
177
  /**
115
178
  * Returns app-level search paths for a file type (relative to project root).
179
+ * Uses the modern `app/` root (not the legacy `marketplace_builder/` alias).
116
180
  *
117
181
  * @example
118
182
  * getAppPaths(PlatformOSFileType.Partial) // → ['app/views/partials', 'app/lib']
119
- * getAppPaths(PlatformOSFileType.GraphQL) // → ['app/graphql']
183
+ * getAppPaths(PlatformOSFileType.GraphQL) // → ['app/graphql', 'app/graph_queries']
120
184
  */
121
185
  export function getAppPaths(type: PlatformOSFileType): string[] {
122
186
  return FILE_TYPE_DIRS[type].map((dir) => `app/${dir}`);
@@ -134,9 +198,7 @@ export function getAppPaths(type: PlatformOSFileType): string[] {
134
198
  * 'modules/core/public/views/partials',
135
199
  * 'modules/core/private/views/partials',
136
200
  * 'app/modules/core/public/lib',
137
- * 'app/modules/core/private/lib',
138
- * 'modules/core/public/lib',
139
- * 'modules/core/private/lib',
201
+ * ...
140
202
  * ]
141
203
  */
142
204
  export function getModulePaths(type: PlatformOSFileType, moduleName: string): string[] {
@@ -158,6 +220,15 @@ export function isKnownLiquidFile(uri: UriString): boolean {
158
220
  return type !== undefined && LIQUID_FILE_TYPES.has(type);
159
221
  }
160
222
 
223
+ /**
224
+ * Returns true if the URI belongs to a recognized platformOS GraphQL directory
225
+ * and should be linted. Files outside known directories (e.g. generator
226
+ * templates, schema files, ERB templates) return false and are excluded.
227
+ */
228
+ export function isKnownGraphQLFile(uri: UriString): boolean {
229
+ return getFileType(uri) === PlatformOSFileType.GraphQL;
230
+ }
231
+
161
232
  export function isPartial(uri: UriString): boolean {
162
233
  return getFileType(uri) === PlatformOSFileType.Partial;
163
234
  }
@@ -189,3 +260,7 @@ export function isSms(uri: UriString): boolean {
189
260
  export function isMigration(uri: UriString): boolean {
190
261
  return getFileType(uri) === PlatformOSFileType.Migration;
191
262
  }
263
+
264
+ export function isFormConfiguration(uri: UriString): boolean {
265
+ return getFileType(uri) === PlatformOSFileType.FormConfiguration;
266
+ }