1
0

Importer-Exporter.st 21 KB

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