@@ -3,14 +3,17 @@ import { service } from '@ember/service';
33import type * as BaseCommandModule from 'https://cardstack.com/base/command' ;
44
55import HostBaseCommand from '../lib/host-base-command' ;
6+ import { findNonConflictingFilename } from '../utils/file-name' ;
67
7- import type NetworkService from '../services/network' ;
8+ import type CardService from '../services/card-service' ;
9+ import type { SaveType } from '../services/card-service' ;
810import type RealmService from '../services/realm' ;
911
1012export default class WriteTextFileCommand extends HostBaseCommand <
11- typeof BaseCommandModule . WriteTextFileInput
13+ typeof BaseCommandModule . WriteTextFileInput ,
14+ typeof BaseCommandModule . FileUrlCard
1215> {
13- @service declare private network : NetworkService ;
16+ @service declare private cardService : CardService ;
1417 @service declare private realm : RealmService ;
1518
1619 description = `Write a text file to a realm, such as a module or a card.` ;
@@ -26,40 +29,74 @@ export default class WriteTextFileCommand extends HostBaseCommand<
2629
2730 protected async run (
2831 input : BaseCommandModule . WriteTextFileInput ,
29- ) : Promise < undefined > {
32+ ) : Promise < BaseCommandModule . FileUrlCard > {
33+ if ( input . overwrite && input . useNonConflictingFilename ) {
34+ throw new Error (
35+ 'Cannot use both overwrite and useNonConflictingFilename.' ,
36+ ) ;
37+ }
3038 let realm ;
3139 if ( input . realm ) {
3240 realm = this . realm . realmOfURL ( new URL ( input . realm ) ) ;
3341 if ( ! realm ) {
3442 throw new Error ( `Invalid or unknown realm provided: ${ input . realm } ` ) ;
3543 }
3644 }
37- if ( input . path . startsWith ( '/' ) ) {
38- input . path = input . path . slice ( 1 ) ;
45+ let path = input . path ;
46+ if ( path . startsWith ( '/' ) ) {
47+ path = path . slice ( 1 ) ;
3948 }
40- let url = new URL ( input . path , realm ?. href ) ;
49+ let url = new URL ( path , realm ?. href ) ;
50+ let finalUrl = url ;
51+ let shouldWrite = true ;
4152 if ( ! input . overwrite ) {
42- let existing = await this . network . authedFetch ( url ) ;
43-
44- if ( existing . ok || existing . status === 406 ) {
45- throw new Error ( `File already exists: ${ input . path } ` ) ;
46- }
53+ if ( input . useNonConflictingFilename ) {
54+ let existing = await this . cardService . getSource ( url ) ;
55+ if ( existing . status === 404 ) {
56+ shouldWrite = true ;
57+ } else if ( existing . status === 200 ) {
58+ if ( existing . content . trim ( ) !== '' ) {
59+ let nonConflictingUrl = await findNonConflictingFilename (
60+ url . href ,
61+ ( candidateUrl ) => this . fileExists ( candidateUrl ) ,
62+ ) ;
63+ finalUrl = new URL ( nonConflictingUrl ) ;
64+ } else {
65+ shouldWrite = input . content . trim ( ) !== '' ;
66+ }
67+ } else {
68+ throw new Error (
69+ `Error checking if file exists at ${ url } : ${ existing . status } ` ,
70+ ) ;
71+ }
72+ } else {
73+ let existing = await this . cardService . getSource ( url ) ;
74+ if ( existing . status === 200 || existing . status === 406 ) {
75+ throw new Error ( `File already exists: ${ path } ` ) ;
76+ }
4777
48- if ( existing . status !== 404 ) {
49- throw new Error (
50- `Error checking if file exists at ${ input . path } : ${ existing . statusText } (${ existing . status } )` ,
51- ) ;
78+ if ( existing . status !== 404 ) {
79+ let errorDetails = existing . content ?. trim ( )
80+ ? `${ existing . content } (${ existing . status } )`
81+ : `${ existing . status } ` ;
82+ throw new Error (
83+ `Error checking if file exists at ${ path } : ${ errorDetails } ` ,
84+ ) ;
85+ }
5286 }
5387 }
54- let response = await this . network . authedFetch ( url , {
55- method : 'POST' ,
56- headers : {
57- Accept : 'application/vnd.card+source' ,
58- } ,
59- body : input . content ,
60- } ) ;
61- if ( ! response . ok ) {
62- throw new Error ( `Failed to write file ${ url } : ${ response . statusText } ` ) ;
88+ if ( shouldWrite ) {
89+ let saveType : SaveType = input . overwrite ? 'editor' : 'create-file' ;
90+ await this . cardService . saveSource ( finalUrl , input . content , saveType ) ;
6391 }
92+
93+ let commandModule = await this . loadCommandModule ( ) ;
94+ const { FileUrlCard } = commandModule ;
95+ return new FileUrlCard ( { fileUrl : finalUrl . href } ) ;
96+ }
97+
98+ private async fileExists ( fileUrl : string ) : Promise < boolean > {
99+ let getSourceResult = await this . cardService . getSource ( new URL ( fileUrl ) ) ;
100+ return getSourceResult . status !== 404 ;
64101 }
65102}
0 commit comments