1
0

Importer-Exporter.st 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803
  1. Smalltalk current createPackage: 'Importer-Exporter'!
  2. Object subclass: #AmdExporter
  3. instanceVariableNames: ''
  4. package: 'Importer-Exporter'!
  5. !AmdExporter class methodsFor: 'exporting-output'!
  6. exportPackageEpilogueOf: aPackage on: aStream
  7. aStream
  8. nextPutAll: '});';
  9. lf
  10. !
  11. exportPackagePrologueOf: aPackage on: aStream
  12. aStream
  13. nextPutAll: 'define("amber/';
  14. nextPutAll: aPackage name;
  15. nextPutAll: '", ["amber_vm/smalltalk","amber_vm/nil","amber_vm/_st"], function(smalltalk,nil,_st){';
  16. lf
  17. !
  18. exportPackageTransportOf: aPackage on: aStream
  19. aStream
  20. nextPutAll: 'smalltalk.packages[';
  21. nextPutAll: aPackage name asJavascript;
  22. nextPutAll: '].transport = ';
  23. nextPutAll: aPackage transportJson;
  24. nextPutAll: ';';
  25. lf
  26. ! !
  27. Object subclass: #ChunkExporter
  28. instanceVariableNames: ''
  29. package: 'Importer-Exporter'!
  30. !ChunkExporter commentStamp!
  31. I am an exporter dedicated to outputting Amber source code in the classic Smalltalk chunk format.
  32. I do not output any compiled code.!
  33. !ChunkExporter class methodsFor: 'exporting-accessing'!
  34. extensionCategoriesOfPackage: package
  35. "Issue #143: sort protocol alphabetically"
  36. | name map result |
  37. name := package name.
  38. result := OrderedCollection new.
  39. (Package sortedClasses: Smalltalk current classes) do: [:each |
  40. {each. each class} do: [:aClass |
  41. map := Dictionary new.
  42. aClass protocolsDo: [:category :methods |
  43. (category match: '^\*', name) ifTrue: [ map at: category put: methods ]].
  44. result addAll: ((map keys sorted: [:a :b | a <= b ]) collect: [:category |
  45. #{ 'methods'->(map at: category). 'name'->category. 'class'->aClass}]) ]].
  46. ^result
  47. !
  48. methodsOfCategory: category
  49. "Issue #143: sort methods alphabetically"
  50. ^(category at: #methods) sorted: [:a :b | a selector <= b selector]
  51. !
  52. ownCategoriesOfClass: aClass
  53. "Issue #143: sort protocol alphabetically"
  54. | map |
  55. map := Dictionary new.
  56. aClass protocolsDo: [:category :methods |
  57. (category match: '^\*') ifFalse: [ map at: category put: methods ]].
  58. ^(map keys sorted: [:a :b | a <= b ]) collect: [:category |
  59. #{
  60. 'methods'->(map at: category).
  61. 'name'->category.
  62. 'class'->aClass }]
  63. !
  64. ownCategoriesOfMetaClass: aClass
  65. "Issue #143: sort protocol alphabetically"
  66. ^self ownCategoriesOfClass: aClass class
  67. ! !
  68. !ChunkExporter class methodsFor: 'exporting-output'!
  69. exportCategoryEpilogueOf: category on: aStream
  70. aStream nextPutAll: ' !!'; lf; lf
  71. !
  72. exportCategoryPrologueOf: category on: aStream
  73. aStream
  74. nextPutAll: '!!', (self classNameFor: (category at: #class));
  75. nextPutAll: ' methodsFor: ''', (category at: #name), '''!!'
  76. !
  77. exportDefinitionOf: aClass on: aStream
  78. "Chunk format."
  79. aStream
  80. nextPutAll: (self classNameFor: aClass superclass);
  81. nextPutAll: ' subclass: #', (self classNameFor: aClass); lf;
  82. tab; nextPutAll: 'instanceVariableNames: '''.
  83. aClass instanceVariableNames
  84. do: [:each | aStream nextPutAll: each]
  85. separatedBy: [aStream nextPutAll: ' '].
  86. aStream
  87. nextPutAll: ''''; lf;
  88. tab; nextPutAll: 'package: ''', aClass category, '''!!'; lf.
  89. aClass comment notEmpty ifTrue: [
  90. aStream
  91. nextPutAll: '!!', (self classNameFor: aClass), ' commentStamp!!';lf;
  92. nextPutAll: (self chunkEscape: aClass comment), '!!';lf].
  93. aStream lf
  94. !
  95. exportMetaDefinitionOf: aClass on: aStream
  96. aClass class instanceVariableNames isEmpty ifFalse: [
  97. aStream
  98. nextPutAll: (self classNameFor: aClass class);
  99. nextPutAll: ' instanceVariableNames: '''.
  100. aClass class instanceVariableNames
  101. do: [:each | aStream nextPutAll: each]
  102. separatedBy: [aStream nextPutAll: ' '].
  103. aStream
  104. nextPutAll: '''!!'; lf; lf]
  105. !
  106. exportMethod: aMethod on: aStream
  107. aStream
  108. lf; lf; nextPutAll: (self chunkEscape: aMethod source); lf;
  109. nextPutAll: '!!'
  110. !
  111. exportPackageDefinitionOf: package on: aStream
  112. "Chunk format."
  113. aStream
  114. nextPutAll: 'Smalltalk current createPackage: ''', package name, '''!!';
  115. lf
  116. ! !
  117. !ChunkExporter class methodsFor: 'fileOut'!
  118. recipe
  119. "Export a given package."
  120. | exportCategoryRecipe |
  121. exportCategoryRecipe := {
  122. self -> #exportCategoryPrologueOf:on:.
  123. {
  124. self -> #methodsOfCategory:.
  125. self -> #exportMethod:on: }.
  126. self -> #exportCategoryEpilogueOf:on: }.
  127. ^{
  128. self -> #exportPackageDefinitionOf:on:.
  129. {
  130. PluggableExporter -> #ownClassesOfPackage:.
  131. self -> #exportDefinitionOf:on:.
  132. { self -> #ownCategoriesOfClass: }, exportCategoryRecipe.
  133. self -> #exportMetaDefinitionOf:on:.
  134. { self -> #ownCategoriesOfMetaClass: }, exportCategoryRecipe }.
  135. { self -> #extensionCategoriesOfPackage: }, exportCategoryRecipe
  136. }
  137. ! !
  138. !ChunkExporter class methodsFor: 'private'!
  139. chunkEscape: aString
  140. "Replace all occurrences of !! with !!!! and trim at both ends."
  141. ^(aString replace: '!!' with: '!!!!') trimBoth
  142. !
  143. classNameFor: aClass
  144. ^aClass isMetaclass
  145. ifTrue: [aClass instanceClass name, ' class']
  146. ifFalse: [
  147. aClass isNil
  148. ifTrue: ['nil']
  149. ifFalse: [aClass name]]
  150. ! !
  151. Object subclass: #ChunkParser
  152. instanceVariableNames: 'stream'
  153. package: 'Importer-Exporter'!
  154. !ChunkParser commentStamp!
  155. I am responsible for parsing aStream contents in the chunk format.
  156. ## API
  157. ChunkParser new
  158. stream: aStream;
  159. nextChunk!
  160. !ChunkParser methodsFor: 'accessing'!
  161. stream: aStream
  162. stream := aStream
  163. ! !
  164. !ChunkParser methodsFor: 'reading'!
  165. nextChunk
  166. "The chunk format (Smalltalk Interchange Format or Fileout format)
  167. is a trivial format but can be a bit tricky to understand:
  168. - Uses the exclamation mark as delimiter of chunks.
  169. - Inside a chunk a normal exclamation mark must be doubled.
  170. - A non empty chunk must be a valid Smalltalk expression.
  171. - A chunk on top level with a preceding empty chunk is an instruction chunk:
  172. - The object created by the expression then takes over reading chunks.
  173. This metod returns next chunk as a String (trimmed), empty String (all whitespace) or nil."
  174. | char result chunk |
  175. result := '' writeStream.
  176. [char := stream next.
  177. char notNil] whileTrue: [
  178. char = '!!' ifTrue: [
  179. stream peek = '!!'
  180. ifTrue: [stream next "skipping the escape double"]
  181. ifFalse: [^result contents trimBoth "chunk end marker found"]].
  182. result nextPut: char].
  183. ^nil "a chunk needs to end with !!"
  184. ! !
  185. !ChunkParser class methodsFor: 'not yet classified'!
  186. on: aStream
  187. ^self new stream: aStream
  188. ! !
  189. Object subclass: #Exporter
  190. instanceVariableNames: ''
  191. package: 'Importer-Exporter'!
  192. !Exporter commentStamp!
  193. I am responsible for outputting Amber code into a JavaScript string.
  194. The generated output is enough to reconstruct the exported data, including Smalltalk source code and other metadata.
  195. ## Use case
  196. I am typically used to save code outside of the Amber runtime (committing to disk, etc.).
  197. ## API
  198. Use `#exportAll`, `#exportClass:` or `#exportPackage:` methods.!
  199. !Exporter class methodsFor: 'exporting-accessing'!
  200. extensionMethodsOfPackage: package
  201. "Issue #143: sort classes and methods alphabetically"
  202. | name result |
  203. name := package name.
  204. result := OrderedCollection new.
  205. (Package sortedClasses: Smalltalk current classes) do: [:each |
  206. {each. each class} do: [:aClass |
  207. result addAll: (((aClass methodDictionary values)
  208. sorted: [:a :b | a selector <= b selector])
  209. select: [:method | method category match: '^\*', name]) ]].
  210. ^result
  211. !
  212. ownMethodsOfClass: aClass
  213. "Issue #143: sort methods alphabetically"
  214. ^((aClass methodDictionary values) sorted: [:a :b | a selector <= b selector])
  215. reject: [:each | (each category match: '^\*')]
  216. !
  217. ownMethodsOfMetaClass: aClass
  218. "Issue #143: sort methods alphabetically"
  219. ^self ownMethodsOfClass: aClass class
  220. ! !
  221. !Exporter class methodsFor: 'exporting-output'!
  222. exportDefinitionOf: aClass on: aStream
  223. aStream
  224. lf;
  225. nextPutAll: 'smalltalk.addClass(';
  226. nextPutAll: '''', (self classNameFor: aClass), ''', ';
  227. nextPutAll: 'smalltalk.', (self classNameFor: aClass superclass);
  228. nextPutAll: ', ['.
  229. aClass instanceVariableNames
  230. do: [:each | aStream nextPutAll: '''', each, '''']
  231. separatedBy: [aStream nextPutAll: ', '].
  232. aStream
  233. nextPutAll: '], ''';
  234. nextPutAll: aClass category, '''';
  235. nextPutAll: ');'.
  236. aClass comment notEmpty ifTrue: [
  237. aStream
  238. lf;
  239. nextPutAll: 'smalltalk.';
  240. nextPutAll: (self classNameFor: aClass);
  241. nextPutAll: '.comment=';
  242. nextPutAll: aClass comment asJavascript;
  243. nextPutAll: ';'].
  244. aStream lf
  245. !
  246. exportMetaDefinitionOf: aClass on: aStream
  247. aStream lf.
  248. aClass class instanceVariableNames isEmpty ifFalse: [
  249. aStream
  250. nextPutAll: 'smalltalk.', (self classNameFor: aClass class);
  251. nextPutAll: '.iVarNames = ['.
  252. aClass class instanceVariableNames
  253. do: [:each | aStream nextPutAll: '''', each, '''']
  254. separatedBy: [aStream nextPutAll: ','].
  255. aStream nextPutAll: '];', String lf]
  256. !
  257. exportMethod: aMethod on: aStream
  258. aStream
  259. nextPutAll: 'smalltalk.addMethod(';lf;
  260. "nextPutAll: aMethod selector asSelector asJavascript, ',';lf;"
  261. nextPutAll: 'smalltalk.method({';lf;
  262. nextPutAll: 'selector: ', aMethod selector asJavascript, ',';lf;
  263. nextPutAll: 'category: ''', aMethod category, ''',';lf;
  264. nextPutAll: 'fn: ', aMethod fn compiledSource, ',';lf;
  265. nextPutAll: 'args: ', aMethod arguments asJavascript, ','; lf;
  266. nextPutAll: 'source: ', aMethod source asJavascript, ',';lf;
  267. nextPutAll: 'messageSends: ', aMethod messageSends asJavascript, ',';lf;
  268. nextPutAll: 'referencedClasses: ', aMethod referencedClasses asJavascript.
  269. aStream
  270. lf;
  271. nextPutAll: '}),';lf;
  272. nextPutAll: 'smalltalk.', (self classNameFor: aMethod methodClass);
  273. nextPutAll: ');';lf;lf
  274. !
  275. exportPackageDefinitionOf: package on: aStream
  276. aStream
  277. nextPutAll: 'smalltalk.addPackage(';
  278. nextPutAll: '''', package name, ''');';
  279. lf
  280. !
  281. exportPackageEpilogueOf: aPackage on: aStream
  282. aStream
  283. nextPutAll: '})(global_smalltalk,global_nil,global__st);';
  284. lf
  285. !
  286. exportPackagePrologueOf: aPackage on: aStream
  287. aStream
  288. nextPutAll: '(function(smalltalk,nil,_st){';
  289. lf
  290. ! !
  291. !Exporter class methodsFor: 'fileOut'!
  292. amdRecipe
  293. "Export a given package with amd transport type."
  294. | legacy |
  295. legacy := self recipe.
  296. ^(legacy copyFrom: 1 to: 2),
  297. { AmdExporter -> #exportPackageTransportOf:on: },
  298. (legacy copyFrom: 3 to: legacy size)
  299. !
  300. recipe
  301. "Export a given package."
  302. ^{
  303. AmdExporter -> #exportPackagePrologueOf:on:.
  304. self -> #exportPackageDefinitionOf:on:.
  305. {
  306. PluggableExporter -> #ownClassesOfPackage:.
  307. self -> #exportDefinitionOf:on:.
  308. {
  309. self -> #ownMethodsOfClass:.
  310. self -> #exportMethod:on: }.
  311. self -> #exportMetaDefinitionOf:on:.
  312. {
  313. self -> #ownMethodsOfMetaClass:.
  314. self -> #exportMethod:on: } }.
  315. {
  316. self -> #extensionMethodsOfPackage:.
  317. self -> #exportMethod:on: }.
  318. AmdExporter -> #exportPackageEpilogueOf:on:
  319. }
  320. ! !
  321. !Exporter class methodsFor: 'private'!
  322. classNameFor: aClass
  323. ^aClass isMetaclass
  324. ifTrue: [aClass instanceClass name, '.klass']
  325. ifFalse: [
  326. aClass isNil
  327. ifTrue: ['nil']
  328. ifFalse: [aClass name]]
  329. ! !
  330. Exporter subclass: #StrippedExporter
  331. instanceVariableNames: ''
  332. package: 'Importer-Exporter'!
  333. !StrippedExporter commentStamp!
  334. I export Amber code into a JavaScript string, but without any optional associated data like the Amber source code.!
  335. !StrippedExporter class methodsFor: 'exporting-output'!
  336. exportDefinitionOf: aClass on: aStream
  337. aStream
  338. lf;
  339. nextPutAll: 'smalltalk.addClass(';
  340. nextPutAll: '''', (self classNameFor: aClass), ''', ';
  341. nextPutAll: 'smalltalk.', (self classNameFor: aClass superclass);
  342. nextPutAll: ', ['.
  343. aClass instanceVariableNames
  344. do: [:each | aStream nextPutAll: '''', each, '''']
  345. separatedBy: [aStream nextPutAll: ', '].
  346. aStream
  347. nextPutAll: '], ''';
  348. nextPutAll: aClass category, '''';
  349. nextPutAll: ');'.
  350. aStream lf
  351. !
  352. exportMethod: aMethod on: aStream
  353. aStream
  354. nextPutAll: 'smalltalk.addMethod(';lf;
  355. "nextPutAll: aMethod selector asSelector asJavascript, ',';lf;"
  356. nextPutAll: 'smalltalk.method({';lf;
  357. nextPutAll: 'selector: ', aMethod selector asJavascript, ',';lf;
  358. nextPutAll: 'fn: ', aMethod fn compiledSource, ',';lf;
  359. nextPutAll: 'messageSends: ', aMethod messageSends asJavascript;
  360. nextPutAll: '}),';lf;
  361. nextPutAll: 'smalltalk.', (self classNameFor: aMethod methodClass);
  362. nextPutAll: ');';lf;lf
  363. ! !
  364. Object subclass: #Importer
  365. instanceVariableNames: ''
  366. package: 'Importer-Exporter'!
  367. !Importer commentStamp!
  368. I can import Amber code from a string in the chunk format.
  369. ## API
  370. Importer new import: aString!
  371. !Importer methodsFor: 'fileIn'!
  372. import: aStream
  373. | chunk result parser lastEmpty |
  374. parser := ChunkParser on: aStream.
  375. lastEmpty := false.
  376. [chunk := parser nextChunk.
  377. chunk isNil] whileFalse: [
  378. chunk isEmpty
  379. ifTrue: [lastEmpty := true]
  380. ifFalse: [
  381. result := Compiler new evaluateExpression: chunk.
  382. lastEmpty
  383. ifTrue: [
  384. lastEmpty := false.
  385. result scanFrom: parser]]]
  386. ! !
  387. Object subclass: #PackageHandler
  388. instanceVariableNames: ''
  389. package: 'Importer-Exporter'!
  390. !PackageHandler commentStamp!
  391. I am responsible for handling package loading and committing.
  392. I should not be used directly. Instead, use the corresponding `Package` methods.!
  393. !PackageHandler methodsFor: 'committing'!
  394. commit: aPackage
  395. self commitChannels
  396. do: [ :commitStrategyFactory || fileContents commitStrategy |
  397. commitStrategy := commitStrategyFactory value: aPackage.
  398. fileContents := String streamContents: [ :stream |
  399. (PluggableExporter newUsing: commitStrategy key) exportPackage: aPackage on: stream ].
  400. self ajaxPutAt: commitStrategy value data: fileContents ]
  401. displayingProgress: 'Committing package ', aPackage name
  402. ! !
  403. !PackageHandler methodsFor: 'private'!
  404. ajaxPutAt: aURL data: aString
  405. jQuery
  406. ajax: aURL
  407. options: #{
  408. 'type' -> 'PUT'.
  409. 'data' -> aString.
  410. 'contentType' -> 'text/plain;charset=UTF-8'.
  411. 'error' -> [ :xhr | self error: 'Commiting ' , aURL , ' failed with reason: "' , (xhr responseText) , '"'] }
  412. ! !
  413. PackageHandler class instanceVariableNames: 'registry'!
  414. !PackageHandler class methodsFor: 'accessing'!
  415. classRegisteredFor: aString
  416. ^registry at: aString
  417. !
  418. for: aString
  419. ^(self classRegisteredFor: aString) new
  420. ! !
  421. !PackageHandler class methodsFor: 'initialization'!
  422. initialize
  423. super initialize.
  424. registry := #{}
  425. ! !
  426. !PackageHandler class methodsFor: 'registry'!
  427. register: aClass for: aString
  428. registry at: aString put: aClass
  429. !
  430. registerFor: aString
  431. PackageHandler register: self for: aString
  432. ! !
  433. PackageHandler subclass: #AmdPackageHandler
  434. instanceVariableNames: ''
  435. package: 'Importer-Exporter'!
  436. !AmdPackageHandler commentStamp!
  437. I am responsible for handling package loading and committing.
  438. I should not be used directly. Instead, use the corresponding `Package` methods.!
  439. !AmdPackageHandler methodsFor: 'committing'!
  440. commitChannels
  441. ^{
  442. [ :pkg | Exporter amdRecipe -> (pkg commitPathJs, '/', pkg name, '.js') ].
  443. [ :pkg | StrippedExporter amdRecipe -> (pkg commitPathJs, '/', pkg name, '.deploy.js') ].
  444. [ :pkg | ChunkExporter recipe -> (pkg commitPathSt, '/', pkg name, '.st') ]
  445. }
  446. !
  447. commitPathJsFor: aPackage
  448. ^self toUrl: (self namespaceFor: aPackage)
  449. !
  450. commitPathStFor: aPackage
  451. | result |
  452. result := self toUrl: (self namespaceFor: aPackage), '/_source'.
  453. ^ (result match: '/_source$') ifTrue: [ nil ] ifFalse: [ result ]
  454. !
  455. namespaceFor: aPackage
  456. ^aPackage amdNamespace
  457. ifNil: [ aPackage amdNamespace: self class defaultNamespace; amdNamespace ]
  458. ! !
  459. !AmdPackageHandler methodsFor: 'private'!
  460. toUrl: aString
  461. (Smalltalk current at: '_amd_require')
  462. ifNil: [ self error: 'AMD loader not present' ]
  463. ifNotNil: [ :require | ^(require basicAt: 'toUrl') value: aString ]
  464. ! !
  465. AmdPackageHandler class instanceVariableNames: 'defaultNamespace'!
  466. !AmdPackageHandler class methodsFor: 'commit paths'!
  467. commitPathsFromLoader
  468. "TODO"
  469. !
  470. defaultNamespace
  471. ^ defaultNamespace ifNil: [ self error: 'AMD default namespace not set.' ]
  472. !
  473. defaultNamespace: aString
  474. defaultNamespace := aString
  475. !
  476. resetCommitPaths
  477. defaultNamespace := nil
  478. ! !
  479. !AmdPackageHandler class methodsFor: 'initialization'!
  480. initialize
  481. super initialize.
  482. self registerFor: 'amd'.
  483. self commitPathsFromLoader
  484. ! !
  485. PackageHandler subclass: #LegacyPackageHandler
  486. instanceVariableNames: ''
  487. package: 'Importer-Exporter'!
  488. !LegacyPackageHandler commentStamp!
  489. I am responsible for handling package loading and committing.
  490. I should not be used directly. Instead, use the corresponding `Package` methods.!
  491. !LegacyPackageHandler methodsFor: 'committing'!
  492. commitChannels
  493. ^{
  494. [ :pkg | Exporter recipe -> (pkg commitPathJs, '/', pkg name, '.js') ].
  495. [ :pkg | StrippedExporter recipe -> (pkg commitPathJs, '/', pkg name, '.deploy.js') ].
  496. [ :pkg | ChunkExporter recipe -> (pkg commitPathSt, '/', pkg name, '.st') ]
  497. }
  498. !
  499. commitPathJsFor: aPackage
  500. ^self class defaultCommitPathJs
  501. !
  502. commitPathStFor: aPackage
  503. ^self class defaultCommitPathSt
  504. ! !
  505. !LegacyPackageHandler methodsFor: 'loading'!
  506. loadPackage: packageName prefix: aString
  507. | url |
  508. url := '/', aString, '/js/', packageName, '.js'.
  509. jQuery
  510. ajax: url
  511. options: #{
  512. 'type' -> 'GET'.
  513. 'dataType' -> 'script'.
  514. 'complete' -> [ :jqXHR :textStatus |
  515. jqXHR readyState = 4
  516. ifTrue: [ self setupPackageNamed: packageName prefix: aString ] ].
  517. 'error' -> [ window alert: 'Could not load package at: ', url ]
  518. }
  519. !
  520. loadPackages: aCollection prefix: aString
  521. aCollection do: [ :each |
  522. self loadPackage: each prefix: aString ]
  523. ! !
  524. !LegacyPackageHandler methodsFor: 'private'!
  525. setupPackageNamed: packageName prefix: aString
  526. (Package named: packageName)
  527. setupClasses;
  528. commitPathJs: '/', aString, '/js';
  529. commitPathSt: '/', aString, '/st'
  530. ! !
  531. LegacyPackageHandler class instanceVariableNames: 'defaultCommitPathJs defaultCommitPathSt'!
  532. !LegacyPackageHandler class methodsFor: 'commit paths'!
  533. commitPathsFromLoader
  534. <
  535. var commitPath = typeof amber !!== 'undefined' && amber.commitPath;
  536. if (!!commitPath) return;
  537. if (commitPath.js) self._defaultCommitPathJs_(commitPath.js);
  538. if (commitPath.st) self._defaultCommitPathSt_(commitPath.st);
  539. >
  540. !
  541. defaultCommitPathJs
  542. ^ defaultCommitPathJs ifNil: [ defaultCommitPathJs := 'js']
  543. !
  544. defaultCommitPathJs: aString
  545. defaultCommitPathJs := aString
  546. !
  547. defaultCommitPathSt
  548. ^ defaultCommitPathSt ifNil: [ defaultCommitPathSt := 'st']
  549. !
  550. defaultCommitPathSt: aString
  551. defaultCommitPathSt := aString
  552. !
  553. resetCommitPaths
  554. defaultCommitPathJs := nil.
  555. defaultCommitPathSt := nil
  556. ! !
  557. !LegacyPackageHandler class methodsFor: 'initialization'!
  558. initialize
  559. super initialize.
  560. self registerFor: 'unknown'.
  561. self commitPathsFromLoader
  562. ! !
  563. !LegacyPackageHandler class methodsFor: 'loading'!
  564. loadPackages: aCollection prefix: aString
  565. ^ self new loadPackages: aCollection prefix: aString
  566. ! !
  567. Object subclass: #PluggableExporter
  568. instanceVariableNames: 'recipe'
  569. package: 'Importer-Exporter'!
  570. !PluggableExporter methodsFor: 'accessing'!
  571. recipe
  572. ^recipe
  573. !
  574. recipe: anArray
  575. recipe := anArray
  576. ! !
  577. !PluggableExporter methodsFor: 'fileOut'!
  578. export: anObject usingRecipe: anArray on: aStream
  579. | args |
  580. args := { anObject. aStream }.
  581. anArray do: [ :each | | val |
  582. val := each value.
  583. val == each
  584. ifFalse: [ "association"
  585. each key perform: val withArguments: args ]
  586. ifTrue: [ "sub-array"
  587. | selection |
  588. selection := each first key perform: each first value withArguments: { anObject }.
  589. selection do: [ :eachPart | self export: eachPart usingRecipe: each allButFirst on: aStream ]]]
  590. !
  591. exportAll
  592. "Export all packages in the system."
  593. ^String streamContents: [:stream |
  594. Smalltalk current packages do: [:pkg |
  595. self exportPackage: pkg on: stream]]
  596. !
  597. exportPackage: aPackage on: aStream
  598. self export: aPackage usingRecipe: self recipe on: aStream
  599. ! !
  600. !PluggableExporter class methodsFor: 'exporting-accessing'!
  601. newUsing: recipe
  602. ^self new recipe: recipe; yourself
  603. !
  604. ownClassesOfPackage: package
  605. "Export classes in dependency order.
  606. Update (issue #171): Remove duplicates for export"
  607. ^package sortedClasses asSet
  608. ! !
  609. !Package methodsFor: '*Importer-Exporter'!
  610. amdNamespace
  611. <return (self.transport && self.transport.amdNamespace) || nil>
  612. !
  613. amdNamespace: aString
  614. <
  615. if (!!self.transport) { self.transport = { type: 'amd' }; }
  616. if (self.transport.type !!== 'amd') { throw new Error('Package '+self._name()+' has transport type '+self.transport.type+', not "amd".'); }
  617. self.transport.amdNamespace = aString;
  618. >
  619. !
  620. commit
  621. ^ self transport commit: self
  622. !
  623. commitPathJs
  624. ^ (extension ifNil: [ extension := #{} ]) at: #commitPathJs ifAbsentPut: [self transport commitPathJsFor: self]
  625. !
  626. commitPathJs: aString
  627. ^ (extension ifNil: [ extension := #{} ]) at: #commitPathJs put: aString
  628. !
  629. commitPathSt
  630. ^ (extension ifNil: [ extension := #{} ]) at: #commitPathSt ifAbsentPut: [self transport commitPathStFor: self]
  631. !
  632. commitPathSt: aString
  633. ^ (extension ifNil: [ extension := #{} ]) at: #commitPathSt put: aString
  634. !
  635. transport
  636. ^ PackageHandler for: self transportType
  637. !
  638. transportJson
  639. <return JSON.stringify(self.transport || null);>
  640. !
  641. transportType
  642. <return (self.transport && self.transport.type) || 'unknown';>
  643. ! !