check-license-header.js 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. #!/usr/bin/env node
  2. // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  3. // SPDX-License-Identifier: Apache-2.0
  4. // SPDX-License-Identifier: MIT
  5. const fs = require('fs')
  6. const path = require('path')
  7. const readline = require('readline')
  8. const header = `Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  9. SPDX-License-Identifier: Apache-2.0
  10. SPDX-License-Identifier: MIT`
  11. const bundlerLicense =
  12. '// Copyright 2016-2019 Cargo-Bundle developers <https://github.com/burtonageo/cargo-bundle>'
  13. const extensions = ['.rs', '.js', '.ts', '.yml', '.swift', '.kt']
  14. const ignore = [
  15. 'target',
  16. 'templates',
  17. 'node_modules',
  18. 'gen',
  19. 'dist',
  20. 'bundle.js',
  21. 'bundle.global.js'
  22. ]
  23. async function checkFile(file) {
  24. if (extensions.some((e) => file.endsWith(e)) && !ignore.some((i) => file.includes(`/${i}/`))) {
  25. const fileStream = fs.createReadStream(file)
  26. const rl = readline.createInterface({
  27. input: fileStream,
  28. crlfDelay: Infinity
  29. })
  30. let contents = ``
  31. let i = 0
  32. for await (let line of rl) {
  33. // ignore empty lines, allow shebang and bundler license
  34. if (
  35. line.length === 0 ||
  36. line.startsWith('#!') ||
  37. line.startsWith('// swift-tools-version:') ||
  38. line === bundlerLicense
  39. ) {
  40. continue
  41. }
  42. // strip comment marker
  43. if (line.startsWith('// ')) {
  44. line = line.substring(3)
  45. } else if (line.startsWith('# ')) {
  46. line = line.substring(2)
  47. }
  48. contents += line
  49. if (++i === 3) {
  50. break
  51. }
  52. contents += '\n'
  53. }
  54. if (contents !== header) {
  55. return true
  56. }
  57. }
  58. return false
  59. }
  60. async function check(src) {
  61. const missingHeader = []
  62. for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
  63. const p = path.join(src, entry.name)
  64. if (entry.isSymbolicLink() || ignore.includes(entry.name)) {
  65. continue
  66. }
  67. if (entry.isDirectory()) {
  68. const missing = await check(p)
  69. missingHeader.push(...missing)
  70. } else {
  71. const isMissing = await checkFile(p)
  72. if (isMissing) {
  73. missingHeader.push(p)
  74. }
  75. }
  76. }
  77. return missingHeader
  78. }
  79. const [_bin, _script, ...files] = process.argv
  80. if (files.length > 0) {
  81. async function run() {
  82. const missing = []
  83. for (const f of files) {
  84. const isMissing = await checkFile(f)
  85. if (isMissing) {
  86. missing.push(f)
  87. }
  88. }
  89. if (missing.length > 0) {
  90. console.log(missing.join('\n'))
  91. process.exit(1)
  92. }
  93. }
  94. run()
  95. } else {
  96. check('.').then((missing) => {
  97. if (missing.length > 0) {
  98. console.log(missing.join('\n'))
  99. process.exit(1)
  100. }
  101. })
  102. }