@@ -16,15 +16,16 @@ import { ExtensionPack, ExtensionPackModelFile } from "./shared/extension-pack";
1616import { NotificationLogger , showAndLogErrorMessage } from "../common/logging" ;
1717import { containsPath } from "../pure/files" ;
1818import { disableAutoNameExtensionPack } from "../config" ;
19+ import {
20+ autoNameExtensionPack ,
21+ ExtensionPackName ,
22+ formatPackName ,
23+ parsePackName ,
24+ validatePackName ,
25+ } from "./extension-pack-name" ;
1926
2027const maxStep = 3 ;
2128
22- const packNamePartRegex = / [ a - z 0 - 9 ] (?: [ a - z 0 - 9 - ] * [ a - z 0 - 9 ] ) ? / ;
23- const packNameRegex = new RegExp (
24- `^(?<scope>${ packNamePartRegex . source } )/(?<name>${ packNamePartRegex . source } )$` ,
25- ) ;
26- const packNameLength = 128 ;
27-
2829export async function pickExtensionPackModelFile (
2930 cliServer : Pick < CodeQLCliServer , "resolveQlpacks" | "resolveExtensions" > ,
3031 databaseItem : Pick < DatabaseItem , "name" | "language" > ,
@@ -265,30 +266,25 @@ async function pickNewExtensionPack(
265266 databaseItem . language ,
266267 ) ;
267268
268- const packName = await window . showInputBox (
269+ const name = await window . showInputBox (
269270 {
270271 title : "Create new extension pack" ,
271272 prompt : "Enter name of extension pack" ,
272- placeHolder : `e.g. ${ examplePackName } ` ,
273+ placeHolder : examplePackName
274+ ? `e.g. ${ formatPackName ( examplePackName ) } `
275+ : "" ,
273276 validateInput : async ( value : string ) : Promise < string | undefined > => {
274- if ( ! value ) {
275- return "Pack name must not be empty" ;
276- }
277-
278- if ( value . length > packNameLength ) {
279- return `Pack name must be no longer than ${ packNameLength } characters` ;
277+ const message = validatePackName ( value ) ;
278+ if ( message ) {
279+ return message ;
280280 }
281281
282- const matches = packNameRegex . exec ( value ) ;
283- if ( ! matches ?. groups ) {
284- if ( ! value . includes ( "/" ) ) {
285- return "Invalid package name: a pack name must contain a slash to separate the scope from the pack name" ;
286- }
287-
288- return "Invalid package name: a pack name must contain only lowercase ASCII letters, ASCII digits, and hyphens" ;
282+ const packName = parsePackName ( value ) ;
283+ if ( ! packName ) {
284+ return "Invalid pack name" ;
289285 }
290286
291- const packPath = join ( workspaceFolder . uri . fsPath , matches . groups . name ) ;
287+ const packPath = join ( workspaceFolder . uri . fsPath , packName . name ) ;
292288 if ( await pathExists ( packPath ) ) {
293289 return `A pack already exists at ${ packPath } ` ;
294290 }
@@ -298,17 +294,16 @@ async function pickNewExtensionPack(
298294 } ,
299295 token ,
300296 ) ;
301- if ( ! packName ) {
297+ if ( ! name ) {
302298 return undefined ;
303299 }
304300
305- const matches = packNameRegex . exec ( packName ) ;
306- if ( ! matches ?. groups ) {
307- return ;
301+ const packName = parsePackName ( name ) ;
302+ if ( ! packName ) {
303+ return undefined ;
308304 }
309305
310- const name = matches . groups . name ;
311- const packPath = join ( workspaceFolder . uri . fsPath , name ) ;
306+ const packPath = join ( workspaceFolder . uri . fsPath , packName . name ) ;
312307
313308 if ( await pathExists ( packPath ) ) {
314309 return undefined ;
@@ -338,7 +333,8 @@ async function autoCreateExtensionPack(
338333 return undefined ;
339334 }
340335
341- const existingExtensionPackPaths = extensionPacksInfo [ packName ] ;
336+ const existingExtensionPackPaths =
337+ extensionPacksInfo [ formatPackName ( packName ) ] ;
342338 // If there is already an extension pack with this name, use it if it is valid
343339 if ( existingExtensionPackPaths ?. length === 1 ) {
344340 let extensionPack : ExtensionPack ;
@@ -347,11 +343,11 @@ async function autoCreateExtensionPack(
347343 } catch ( e : unknown ) {
348344 void showAndLogErrorMessage (
349345 logger ,
350- `Could not read extension pack ${ packName } ` ,
346+ `Could not read extension pack ${ formatPackName ( packName ) } ` ,
351347 {
352- fullMessage : `Could not read extension pack ${ packName } at ${
353- existingExtensionPackPaths [ 0 ]
354- } : ${ getErrorMessage ( e ) } `,
348+ fullMessage : `Could not read extension pack ${ formatPackName (
349+ packName ,
350+ ) } at ${ existingExtensionPackPaths [ 0 ] } : ${ getErrorMessage ( e ) } `,
355351 } ,
356352 ) ;
357353
@@ -366,9 +362,11 @@ async function autoCreateExtensionPack(
366362 if ( existingExtensionPackPaths ?. length > 1 ) {
367363 void showAndLogErrorMessage (
368364 logger ,
369- `Extension pack ${ packName } resolves to multiple paths` ,
365+ `Extension pack ${ formatPackName ( packName ) } resolves to multiple paths` ,
370366 {
371- fullMessage : `Extension pack ${ packName } resolves to multiple paths: ${ existingExtensionPackPaths . join (
367+ fullMessage : `Extension pack ${ formatPackName (
368+ packName ,
369+ ) } resolves to multiple paths: ${ existingExtensionPackPaths . join (
372370 ", " ,
373371 ) } `,
374372 } ,
@@ -377,23 +375,14 @@ async function autoCreateExtensionPack(
377375 return undefined ;
378376 }
379377
380- const matches = packNameRegex . exec ( packName ) ;
381- if ( ! matches ?. groups ) {
382- void showAndLogErrorMessage (
383- logger ,
384- `Extension pack ${ packName } does not have a valid name` ,
385- ) ;
386-
387- return undefined ;
388- }
389-
390- const unscopedName = matches . groups . name ;
391- const packPath = join ( workspaceFolder . uri . fsPath , unscopedName ) ;
378+ const packPath = join ( workspaceFolder . uri . fsPath , packName . name ) ;
392379
393380 if ( await pathExists ( packPath ) ) {
394381 void showAndLogErrorMessage (
395382 logger ,
396- `Directory ${ packPath } already exists for extension pack ${ packName } ` ,
383+ `Directory ${ packPath } already exists for extension pack ${ formatPackName (
384+ packName ,
385+ ) } `,
397386 ) ;
398387
399388 return undefined ;
@@ -449,15 +438,15 @@ async function askForWorkspaceFolder(): Promise<WorkspaceFolder | undefined> {
449438
450439async function writeExtensionPack (
451440 packPath : string ,
452- packName : string ,
441+ packName : ExtensionPackName ,
453442 language : string ,
454443) : Promise < ExtensionPack > {
455444 const packYamlPath = join ( packPath , "codeql-pack.yml" ) ;
456445
457446 const extensionPack : ExtensionPack = {
458447 path : packPath ,
459448 yamlPath : packYamlPath ,
460- name : packName ,
449+ name : formatPackName ( packName ) ,
461450 version : "0.0.0" ,
462451 extensionTargets : {
463452 [ `codeql/${ language } -all` ] : "*" ,
@@ -563,40 +552,3 @@ async function readExtensionPack(path: string): Promise<ExtensionPack> {
563552 dataExtensions,
564553 } ;
565554}
566-
567- function autoNameExtensionPack (
568- name : string ,
569- language : string ,
570- ) : string | undefined {
571- let packName = `${ name } -${ language } ` ;
572- if ( ! packName . includes ( "/" ) ) {
573- packName = `pack/${ packName } ` ;
574- }
575-
576- const parts = packName . split ( "/" ) ;
577- const sanitizedParts = parts . map ( ( part ) => sanitizeExtensionPackName ( part ) ) ;
578-
579- // This will ensure there's only 1 slash
580- packName = `${ sanitizedParts [ 0 ] } /${ sanitizedParts . slice ( 1 ) . join ( "-" ) } ` ;
581-
582- return packName ;
583- }
584-
585- function sanitizeExtensionPackName ( name : string ) {
586- // Lowercase everything
587- name = name . toLowerCase ( ) ;
588-
589- // Replace all spaces, dots, and underscores with hyphens
590- name = name . replaceAll ( / [ \s . _ ] + / g, "-" ) ;
591-
592- // Replace all characters which are not allowed by empty strings
593- name = name . replaceAll ( / [ ^ a - z 0 - 9 - ] / g, "" ) ;
594-
595- // Remove any leading or trailing hyphens
596- name = name . replaceAll ( / ^ - | - $ / g, "" ) ;
597-
598- // Remove any duplicate hyphens
599- name = name . replaceAll ( / - { 2 , } / g, "-" ) ;
600-
601- return name ;
602- }
0 commit comments